Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Use hidden window to process flutter messages #24232

Merged
merged 1 commit into from
Mar 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -1538,6 +1538,8 @@ FILE: ../../../flutter/shell/platform/windows/system_utils_winuwp.cc
FILE: ../../../flutter/shell/platform/windows/task_runner.h
FILE: ../../../flutter/shell/platform/windows/task_runner_win32.cc
FILE: ../../../flutter/shell/platform/windows/task_runner_win32.h
FILE: ../../../flutter/shell/platform/windows/task_runner_win32_window.cc
FILE: ../../../flutter/shell/platform/windows/task_runner_win32_window.h
FILE: ../../../flutter/shell/platform/windows/task_runner_winuwp.cc
FILE: ../../../flutter/shell/platform/windows/task_runner_winuwp.h
FILE: ../../../flutter/shell/platform/windows/text_input_manager_win32.cc
Expand Down
2 changes: 2 additions & 0 deletions shell/platform/windows/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ source_set("flutter_windows_source") {
"system_utils_win32.cc",
"task_runner_win32.cc",
"task_runner_win32.h",
"task_runner_win32_window.cc",
"task_runner_win32_window.h",
"text_input_manager_win32.cc",
"text_input_manager_win32.h",
"window_proc_delegate_manager_win32.cc",
Expand Down
6 changes: 1 addition & 5 deletions shell/platform/windows/flutter_windows_win32.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

#include "flutter/shell/platform/windows/dpi_utils_win32.h"
#include "flutter/shell/platform/windows/flutter_window_win32.h"
#include "flutter/shell/platform/windows/task_runner_win32.h"

// Returns the engine corresponding to the given opaque API handle.
static flutter::FlutterWindowsEngine* EngineFromHandle(
Expand Down Expand Up @@ -69,10 +68,7 @@ bool FlutterDesktopViewControllerHandleTopLevelWindowProc(
}

uint64_t FlutterDesktopEngineProcessMessages(FlutterDesktopEngineRef engine) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we even need FlutterDesktopEngineProcessMessages any more with this change. I don't believe we use it in UWP. It would be a breaking change tho.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not necessary anymore (it a noop that just returns -1). Next step would be to remove this from the Windows template (it would become simple Get/Translate/Dispatch message loop). Returning -1 ensures that existing run-loops work just fine after the change.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually removing the function would involve writing a migrator into the Flutter tool. (There's some infrastructure in the tooling for it, but it hasn't been used yet on Windows)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stuartmorgan, I don't think I'd want to remove the function from API anytime soon, but the template can be significantly simplified. I'll look at the migration, but that should not block this PR, right?

Copy link
Contributor

@stuartmorgan-g stuartmorgan-g Feb 27, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, both this PR and a template upgrade are non-breaking, so aren't blocked. It's only at the point where it would actually be removed that a migrator would be necessary.

return static_cast<flutter::TaskRunnerWin32*>(
EngineFromHandle(engine)->task_runner())
->ProcessTasks()
.count();
return std::numeric_limits<uint64_t>::max();
}

void FlutterDesktopPluginRegistrarRegisterTopLevelWindowProcDelegate(
Expand Down
3 changes: 3 additions & 0 deletions shell/platform/windows/public/flutter_windows.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,9 @@ FLUTTER_EXPORT bool FlutterDesktopEngineRun(FlutterDesktopEngineRef engine,
const char* entry_point);

#ifndef WINUWP
// DEPRECATED: This is no longer necessary to call, Flutter will take care of
// processing engine messages transparently through DispatchMessage.
//
// Processes any pending events in the Flutter engine, and returns the
// number of nanoseconds until the next scheduled event (or max, if none).
//
Expand Down
13 changes: 8 additions & 5 deletions shell/platform/windows/task_runner_win32.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,14 @@ TaskRunnerWin32::TaskRunnerWin32(DWORD main_thread_id,
const TaskExpiredCallback& on_task_expired)
: main_thread_id_(main_thread_id),
get_current_time_(get_current_time),
on_task_expired_(std::move(on_task_expired)) {}
on_task_expired_(std::move(on_task_expired)) {
task_runner_window_ = TaskRunnerWin32Window::GetSharedInstance();
task_runner_window_->AddDelegate(this);
}

TaskRunnerWin32::~TaskRunnerWin32() = default;
TaskRunnerWin32::~TaskRunnerWin32() {
task_runner_window_->RemoveDelegate(this);
}

bool TaskRunnerWin32::RunsTasksOnCurrentThread() const {
return GetCurrentThreadId() == main_thread_id_;
Expand Down Expand Up @@ -116,9 +121,7 @@ void TaskRunnerWin32::EnqueueTask(Task task) {
// the lock here momentarily till the end of the scope is a pessimization.
}

if (!PostThreadMessage(main_thread_id_, WM_NULL, 0, 0)) {
std::cerr << "Failed to post message to main thread." << std::endl;
}
task_runner_window_->WakeUp();
}

} // namespace flutter
8 changes: 6 additions & 2 deletions shell/platform/windows/task_runner_win32.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@

#include "flutter/shell/platform/embedder/embedder.h"
#include "flutter/shell/platform/windows/task_runner.h"
#include "flutter/shell/platform/windows/task_runner_win32_window.h"

namespace flutter {

// A custom task runner that integrates with user32 GetMessage semantics so that
// host app can own its own message loop and flutter still gets to process
// tasks on a timely basis.
class TaskRunnerWin32 : public TaskRunner {
class TaskRunnerWin32 : public TaskRunner,
public TaskRunnerWin32Window::Delegate {
public:
// Creates a new task runner with the given main thread ID, current time
// provider, and callback for tasks that are ready to be run.
Expand All @@ -43,7 +45,8 @@ class TaskRunnerWin32 : public TaskRunner {
// |TaskRunner|
void PostTask(TaskClosure task) override;

std::chrono::nanoseconds ProcessTasks();
// |TaskRunnerWin32Window::Delegate|
std::chrono::nanoseconds ProcessTasks() override;

private:
typedef std::variant<FlutterTask, TaskClosure> TaskVariant;
Expand Down Expand Up @@ -75,6 +78,7 @@ class TaskRunnerWin32 : public TaskRunner {
TaskExpiredCallback on_task_expired_;
std::mutex task_queue_mutex_;
std::priority_queue<Task, std::deque<Task>, Task::Comparer> task_queue_;
std::shared_ptr<TaskRunnerWin32Window> task_runner_window_;

TaskRunnerWin32(const TaskRunnerWin32&) = delete;

Expand Down
137 changes: 137 additions & 0 deletions shell/platform/windows/task_runner_win32_window.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// 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/task_runner_win32_window.h"

#include <algorithm>
#include <iostream>

namespace flutter {

TaskRunnerWin32Window::TaskRunnerWin32Window() {
WNDCLASS window_class = RegisterWindowClass();
window_handle_ =
CreateWindowEx(0, window_class.lpszClassName, L"", 0, 0, 0, 0, 0,
HWND_MESSAGE, nullptr, window_class.hInstance, nullptr);

if (window_handle_) {
SetWindowLongPtr(window_handle_, GWLP_USERDATA,
reinterpret_cast<LONG_PTR>(this));
} else {
auto error = GetLastError();
LPWSTR message = nullptr;
size_t size = FormatMessageW(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<LPWSTR>(&message), 0, NULL);
OutputDebugString(message);
LocalFree(message);
}
}

TaskRunnerWin32Window::~TaskRunnerWin32Window() {
if (window_handle_) {
DestroyWindow(window_handle_);
window_handle_ = nullptr;
}
UnregisterClass(window_class_name_.c_str(), nullptr);
}

std::shared_ptr<TaskRunnerWin32Window>
TaskRunnerWin32Window::GetSharedInstance() {
static std::weak_ptr<TaskRunnerWin32Window> instance;
auto res = instance.lock();
if (!res) {
// can't use make_shared with private contructor
res.reset(new TaskRunnerWin32Window());
instance = res;
}
return res;
}

void TaskRunnerWin32Window::WakeUp() {
if (!PostMessage(window_handle_, WM_NULL, 0, 0)) {
std::cerr << "Failed to post message to main thread." << std::endl;
}
}

void TaskRunnerWin32Window::AddDelegate(Delegate* delegate) {
delegates_.push_back(delegate);
SetTimer(std::chrono::nanoseconds::zero());
}

void TaskRunnerWin32Window::RemoveDelegate(Delegate* delegate) {
auto i = std::find(delegates_.begin(), delegates_.end(), delegate);
if (i != delegates_.end()) {
delegates_.erase(i);
}
}

void TaskRunnerWin32Window::ProcessTasks() {
auto next = std::chrono::nanoseconds::max();
auto delegates_copy(delegates_);
for (auto delegate : delegates_copy) {
// if not removed in the meanwhile
if (std::find(delegates_.begin(), delegates_.end(), delegate) !=
delegates_.end()) {
next = std::min(next, delegate->ProcessTasks());
}
}
SetTimer(next);
}

void TaskRunnerWin32Window::SetTimer(std::chrono::nanoseconds when) {
if (when == std::chrono::nanoseconds::max()) {
KillTimer(window_handle_, 0);
} else {
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(when);
::SetTimer(window_handle_, 0, millis.count() + 1, nullptr);
}
}

WNDCLASS TaskRunnerWin32Window::RegisterWindowClass() {
window_class_name_ = L"FlutterTaskRunnerWindow";

WNDCLASS window_class{};
window_class.hCursor = nullptr;
window_class.lpszClassName = window_class_name_.c_str();
window_class.style = 0;
window_class.cbClsExtra = 0;
window_class.cbWndExtra = 0;
window_class.hInstance = GetModuleHandle(nullptr);
window_class.hIcon = nullptr;
window_class.hbrBackground = 0;
window_class.lpszMenuName = nullptr;
window_class.lpfnWndProc = WndProc;
RegisterClass(&window_class);
return window_class;
}

LRESULT
TaskRunnerWin32Window::HandleMessage(UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept {
switch (message) {
case WM_TIMER:
case WM_NULL:
ProcessTasks();
return 0;
}
return DefWindowProcW(window_handle_, message, wparam, lparam);
}

LRESULT TaskRunnerWin32Window::WndProc(HWND const window,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept {
if (auto* that = reinterpret_cast<TaskRunnerWin32Window*>(
GetWindowLongPtr(window, GWLP_USERDATA))) {
return that->HandleMessage(message, wparam, lparam);
} else {
return DefWindowProc(window, message, wparam, lparam);
}
}

} // namespace flutter
60 changes: 60 additions & 0 deletions shell/platform/windows/task_runner_win32_window.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// 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_TASK_RUNNER_WIN32_WINDOW_H_
#define FLUTTER_SHELL_PLATFORM_WINDOWS_TASK_RUNNER_WIN32_WINDOW_H_

#include <windows.h>

#include <chrono>
#include <memory>
#include <string>
#include <vector>

namespace flutter {

// Hidden HWND responsible for processing flutter tasks on main thread
class TaskRunnerWin32Window {
public:
class Delegate {
public:
virtual std::chrono::nanoseconds ProcessTasks() = 0;
};

static std::shared_ptr<TaskRunnerWin32Window> GetSharedInstance();

// Triggers processing delegate tasks on main thread
void WakeUp();

void AddDelegate(Delegate* delegate);
void RemoveDelegate(Delegate* delegate);

~TaskRunnerWin32Window();

private:
TaskRunnerWin32Window();

void ProcessTasks();

void SetTimer(std::chrono::nanoseconds when);

WNDCLASS RegisterWindowClass();

LRESULT
HandleMessage(UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept;

static LRESULT CALLBACK WndProc(HWND const window,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept;

HWND window_handle_;
std::wstring window_class_name_;
std::vector<Delegate*> delegates_;
};
} // namespace flutter

#endif