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

[Windows, Keyboard] Lift key event redispatching to KeyboardManagerWin32 #30702

Merged
merged 12 commits into from
Jan 14, 2022
Merged
9 changes: 5 additions & 4 deletions shell/platform/windows/flutter_window_win32.cc
Original file line number Diff line number Diff line change
Expand Up @@ -184,14 +184,15 @@ void FlutterWindowWin32::OnText(const std::u16string& text) {
binding_handler_delegate_->OnText(text);
}

bool FlutterWindowWin32::OnKey(int key,
void FlutterWindowWin32::OnKey(int key,
int scancode,
int action,
char32_t character,
bool extended,
bool was_down) {
return binding_handler_delegate_->OnKey(key, scancode, action, character,
extended, was_down);
bool was_down,
KeyEventCallback callback) {
binding_handler_delegate_->OnKey(key, scancode, action, character, extended,
was_down, std::move(callback));
}

void FlutterWindowWin32::OnComposeBegin() {
Expand Down
5 changes: 3 additions & 2 deletions shell/platform/windows/flutter_window_win32.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,13 @@ class FlutterWindowWin32 : public WindowWin32, public WindowBindingHandler {
void OnText(const std::u16string& text) override;

// |WindowWin32|
bool OnKey(int key,
void OnKey(int key,
int scancode,
int action,
char32_t character,
bool extended,
bool was_down) override;
bool was_down,
KeyEventCallback callback) override;

// |WindowWin32|
void OnComposeBegin() override;
Expand Down
131 changes: 63 additions & 68 deletions shell/platform/windows/flutter_window_win32_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,28 +34,23 @@ static constexpr int32_t kDefaultPointerDeviceId = 0;
// key event handler.
class SpyKeyboardKeyHandler : public KeyboardHandlerBase {
public:
SpyKeyboardKeyHandler(flutter::BinaryMessenger* messenger,
KeyboardKeyHandler::EventDispatcher dispatch_event) {
real_implementation_ = std::make_unique<KeyboardKeyHandler>(dispatch_event);
SpyKeyboardKeyHandler(flutter::BinaryMessenger* messenger) {
real_implementation_ = std::make_unique<KeyboardKeyHandler>();
real_implementation_->AddDelegate(
std::make_unique<KeyboardKeyChannelHandler>(messenger));
ON_CALL(*this, KeyboardHook(_, _, _, _, _, _))
ON_CALL(*this, KeyboardHook(_, _, _, _, _, _, _))
.WillByDefault(Invoke(real_implementation_.get(),
&KeyboardKeyHandler::KeyboardHook));
}

MOCK_METHOD6(KeyboardHook,
bool(int key,
MOCK_METHOD7(KeyboardHook,
void(int key,
int scancode,
int action,
char32_t character,
bool extended,
bool was_down));
MOCK_METHOD0(ComposeBeginHook, void());
MOCK_METHOD0(ComposeCommitHook, void());
MOCK_METHOD0(ComposeEndHook, void());
MOCK_METHOD2(ComposeChangeHook,
void(const std::u16string& text, int cursor_pos));
bool was_down,
KeyEventCallback callback));

private:
std::unique_ptr<KeyboardKeyHandler> real_implementation_;
Expand Down Expand Up @@ -101,7 +96,10 @@ class SpyTextInputPlugin : public TextInputPlugin,
class MockFlutterWindowWin32 : public FlutterWindowWin32,
public MockMessageQueue {
public:
MockFlutterWindowWin32() : FlutterWindowWin32(800, 600) {
MockFlutterWindowWin32(WPARAM virtual_key = 0, bool is_printable = true)
: virtual_key_(virtual_key),
is_printable_(is_printable),
FlutterWindowWin32(800, 600) {
ON_CALL(*this, GetDpiScale())
.WillByDefault(Return(this->FlutterWindowWin32::GetDpiScale()));
}
Expand Down Expand Up @@ -151,27 +149,68 @@ class MockFlutterWindowWin32 : public FlutterWindowWin32,
MOCK_METHOD0(OnResetImeComposing, void());

protected:
virtual BOOL Win32PeekMessage(LPMSG lpMsg,
UINT wMsgFilterMin,
UINT wMsgFilterMax,
UINT wRemoveMsg) override {
// |KeyboardManagerWin32::WindowDelegate|
BOOL Win32PeekMessage(LPMSG lpMsg,
UINT wMsgFilterMin,
UINT wMsgFilterMax,
UINT wRemoveMsg) override {
return MockMessageQueue::Win32PeekMessage(lpMsg, wMsgFilterMin,
wMsgFilterMax, wRemoveMsg);
}

// |KeyboardManagerWin32::WindowDelegate|
LRESULT Win32DefWindowProc(HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam) override {
return kWmResultDefault;
}

private:
// |KeyboardManagerWin32::WindowDelegate|
UINT Win32DispatchEvent(UINT cInputs, LPINPUT pInputs, int cbSize) override {
for (UINT input_idx = 0; input_idx < cInputs; input_idx += 1) {
SendInput(pInputs[input_idx].ki);
}
return 1;
}

// |MockMessageQueue|
LRESULT Win32SendMessage(UINT const message,
WPARAM const wparam,
LPARAM const lparam) override {
return HandleMessage(message, wparam, lparam);
}

private:
UINT SendInput(KEYBDINPUT kbdinput) {
// Simulate the event loop by just sending the event sent to
// "SendInput" directly to the window.
const bool is_key_up = kbdinput.dwFlags & KEYEVENTF_KEYUP;
const UINT message = is_key_up ? WM_KEYUP : WM_KEYDOWN;

const LPARAM lparam = CreateKeyEventLparam(
kbdinput.wScan, kbdinput.dwFlags & KEYEVENTF_EXTENDEDKEY, is_key_up);
// Windows would normally fill in the virtual key code for us, so we
// simulate it for the test with the key we know is in the test. The
// KBDINPUT we're passed doesn't have it filled in (on purpose, so that
// Windows will fill it in).
//
// TODO(dkwingsmt): Don't check the message results for redispatched
// messages for now, because making them work takes non-trivial rework
// to our current structure. https://github.com/flutter/flutter/issues/87843
// If this is resolved, change them to kWmResultDefault.
pending_responds_.push_back(
Win32Message{message, virtual_key_, lparam, kWmResultDontCheck});
if (is_printable_ && (kbdinput.dwFlags & KEYEVENTF_KEYUP) == 0) {
pending_responds_.push_back(
Win32Message{WM_CHAR, virtual_key_, lparam, kWmResultDontCheck});
}
return 1;
}

std::vector<Win32Message> pending_responds_;
WPARAM virtual_key_;
bool is_printable_;
};

class MockWindowBindingHandlerDelegate : public WindowBindingHandlerDelegate {
Expand Down Expand Up @@ -201,7 +240,8 @@ class MockWindowBindingHandlerDelegate : public WindowBindingHandlerDelegate {
FlutterPointerMouseButtons));
MOCK_METHOD2(OnPointerLeave, void(FlutterPointerDeviceKind, int32_t));
MOCK_METHOD1(OnText, void(const std::u16string&));
MOCK_METHOD6(OnKey, bool(int, int, int, char32_t, bool, bool));
MOCK_METHOD7(OnKey,
void(int, int, int, char32_t, bool, bool, KeyEventCallback));
MOCK_METHOD0(OnComposeBegin, void());
MOCK_METHOD0(OnComposeCommit, void());
MOCK_METHOD0(OnComposeEnd, void());
Expand All @@ -223,32 +263,19 @@ class MockWindowBindingHandlerDelegate : public WindowBindingHandlerDelegate {
// to register the keyboard hook handlers that can be spied upon.
class TestFlutterWindowsView : public FlutterWindowsView {
public:
TestFlutterWindowsView(std::unique_ptr<WindowBindingHandler> window_binding,
WPARAM virtual_key,
bool is_printable = true)
: FlutterWindowsView(std::move(window_binding)),
virtual_key_(virtual_key),
is_printable_(is_printable) {}
TestFlutterWindowsView(std::unique_ptr<WindowBindingHandler> window_binding)
: FlutterWindowsView(std::move(window_binding)) {}

SpyKeyboardKeyHandler* key_event_handler;
SpyTextInputPlugin* text_input_plugin;

void InjectPendingEvents(MockFlutterWindowWin32* win32window) {
win32window->InjectMessageList(pending_responds_.size(),
pending_responds_.data());
pending_responds_.clear();
}

protected:
std::unique_ptr<KeyboardHandlerBase> CreateKeyboardKeyHandler(
flutter::BinaryMessenger* messenger,
flutter::KeyboardKeyHandler::EventDispatcher dispatch_event,
flutter::KeyboardKeyEmbedderHandler::GetKeyStateHandler get_key_state)
override {
auto spy_key_event_handler = std::make_unique<SpyKeyboardKeyHandler>(
messenger, [this](UINT cInputs, LPINPUT pInputs, int cbSize) -> UINT {
return this->SendInput(cInputs, pInputs, cbSize);
});
auto spy_key_event_handler =
std::make_unique<SpyKeyboardKeyHandler>(messenger);
key_event_handler = spy_key_event_handler.get();
return spy_key_event_handler;
}
Expand All @@ -260,38 +287,6 @@ class TestFlutterWindowsView : public FlutterWindowsView {
text_input_plugin = spy_key_event_handler.get();
return spy_key_event_handler;
}

private:
UINT SendInput(UINT cInputs, LPINPUT pInputs, int cbSize) {
// Simulate the event loop by just sending the event sent to
// "SendInput" directly to the window.
const KEYBDINPUT kbdinput = pInputs->ki;
const bool is_key_up = kbdinput.dwFlags & KEYEVENTF_KEYUP;
const UINT message = is_key_up ? WM_KEYUP : WM_KEYDOWN;

const LPARAM lparam = CreateKeyEventLparam(
kbdinput.wScan, kbdinput.dwFlags & KEYEVENTF_EXTENDEDKEY, is_key_up);
// Windows would normally fill in the virtual key code for us, so we
// simulate it for the test with the key we know is in the test. The
// KBDINPUT we're passed doesn't have it filled in (on purpose, so that
// Windows will fill it in).
//
// TODO(dkwingsmt): Don't check the message results for redispatched
// messages for now, because making them work takes non-trivial rework
// to our current structure. https://github.com/flutter/flutter/issues/87843
// If this is resolved, change them to kWmResultDefault.
pending_responds_.push_back(
Win32Message{message, virtual_key_, lparam, kWmResultDontCheck});
if (is_printable_ && (kbdinput.dwFlags & KEYEVENTF_KEYUP) == 0) {
pending_responds_.push_back(
Win32Message{WM_CHAR, virtual_key_, lparam, kWmResultDontCheck});
}
return 1;
}

std::vector<Win32Message> pending_responds_;
WPARAM virtual_key_;
bool is_printable_;
};

// The static value to return as the "handled" value from the framework for key
Expand Down
16 changes: 10 additions & 6 deletions shell/platform/windows/flutter_window_winuwp.cc
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ winrt::Windows::UI::Core::CoreCursor GetCursorByName(
}
}

// UWP does not care if key events are handled, since it can not redispatch them
Copy link
Contributor

Choose a reason for hiding this comment

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

So, does this mean that UWP considers all of the events that go to the framework as handled, or as not handled? Or is it somehow responding synchronously?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

UWP can't redispatch events at all, so it's equivalent to handling all events (outside native components can't receive any events.) Or we can always pass events to native components.

Copy link
Contributor

Choose a reason for hiding this comment

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

Next time you're in the code, could you please add a comment about this somewhere appropriate: just that the default is to handle all events, and why?

// anyway.
static void IgnoreKeyEventResult(bool handled) {}

} // namespace

FlutterWindowWinUWP::FlutterWindowWinUWP(
Expand Down Expand Up @@ -347,9 +351,9 @@ void FlutterWindowWinUWP::OnKeyUp(
unsigned int scancode = status.ScanCode;
int key = static_cast<int>(args.VirtualKey());
int action = 0x0101;
binding_handler_delegate_->OnKey(key, scancode, action, 0,
status.IsExtendedKey /* extended */,
status.WasKeyDown /* was_down */);
binding_handler_delegate_->OnKey(
key, scancode, action, 0, status.IsExtendedKey /* extended */,
status.WasKeyDown /* was_down */, IgnoreKeyEventResult);
}

void FlutterWindowWinUWP::OnKeyDown(
Expand All @@ -363,9 +367,9 @@ void FlutterWindowWinUWP::OnKeyDown(
unsigned int scancode = status.ScanCode;
int key = static_cast<int>(args.VirtualKey());
int action = 0x0100;
binding_handler_delegate_->OnKey(key, scancode, action, 0,
status.IsExtendedKey /* extended */,
status.WasKeyDown /* was_down */);
binding_handler_delegate_->OnKey(
key, scancode, action, 0, status.IsExtendedKey /* extended */,
status.WasKeyDown /* was_down */, IgnoreKeyEventResult);
}

void FlutterWindowWinUWP::OnCharacterReceived(
Expand Down
48 changes: 19 additions & 29 deletions shell/platform/windows/flutter_windows_view.cc
Original file line number Diff line number Diff line change
Expand Up @@ -70,18 +70,8 @@ void FlutterWindowsView::SetEngine(
std::unique_ptr<KeyboardHandlerBase>
FlutterWindowsView::CreateKeyboardKeyHandler(
BinaryMessenger* messenger,
KeyboardKeyHandler::EventDispatcher dispatch_event,
KeyboardKeyEmbedderHandler::GetKeyStateHandler get_key_state) {
// There must be only one handler that receives |SendInput|, i.e. only one
// handler that might redispatch events. (See the documentation of
// |KeyboardKeyHandler| to learn about redispatching.)
//
// Whether an event is a redispatched event is decided by calculating the hash
// of the event. In order to allow the same real event in the future, the
// handler is "toggled" when events pass through, therefore the redispatching
// algorithm does not allow more than 1 handler that takes |SendInput|.
auto keyboard_key_handler =
std::make_unique<KeyboardKeyHandler>(dispatch_event);
auto keyboard_key_handler = std::make_unique<KeyboardKeyHandler>();
keyboard_key_handler->AddDelegate(
std::make_unique<KeyboardKeyEmbedderHandler>(
[this](const FlutterKeyEvent& event, FlutterKeyEventCallback callback,
Expand Down Expand Up @@ -207,13 +197,14 @@ void FlutterWindowsView::OnText(const std::u16string& text) {
SendText(text);
}

bool FlutterWindowsView::OnKey(int key,
void FlutterWindowsView::OnKey(int key,
int scancode,
int action,
char32_t character,
bool extended,
bool was_down) {
return SendKey(key, scancode, action, character, extended, was_down);
bool was_down,
KeyEventCallback callback) {
SendKey(key, scancode, action, character, extended, was_down, callback);
}

void FlutterWindowsView::OnComposeBegin() {
Expand Down Expand Up @@ -267,14 +258,12 @@ void FlutterWindowsView::OnResetImeComposing() {
void FlutterWindowsView::InitializeKeyboard() {
auto internal_plugin_messenger = internal_plugin_registrar_->messenger();
#ifdef WINUWP
KeyboardKeyHandler::EventDispatcher dispatch_event = nullptr;
KeyboardKeyEmbedderHandler::GetKeyStateHandler get_key_state = nullptr;
#else
KeyboardKeyHandler::EventDispatcher dispatch_event = SendInput;
KeyboardKeyEmbedderHandler::GetKeyStateHandler get_key_state = GetKeyState;
#endif
keyboard_key_handler_ = std::move(CreateKeyboardKeyHandler(
internal_plugin_messenger, dispatch_event, get_key_state));
keyboard_key_handler_ = std::move(
CreateKeyboardKeyHandler(internal_plugin_messenger, get_key_state));
text_input_plugin_ =
std::move(CreateTextInputPlugin(internal_plugin_messenger));
}
Expand Down Expand Up @@ -383,21 +372,22 @@ void FlutterWindowsView::SendText(const std::u16string& text) {
text_input_plugin_->TextHook(text);
}

bool FlutterWindowsView::SendKey(int key,
void FlutterWindowsView::SendKey(int key,
int scancode,
int action,
char32_t character,
bool extended,
bool was_down) {
if (keyboard_key_handler_->KeyboardHook(key, scancode, action, character,
extended, was_down)) {
return true;
}

text_input_plugin_->KeyboardHook(key, scancode, action, character, extended,
was_down);

return false;
bool was_down,
KeyEventCallback callback) {
keyboard_key_handler_->KeyboardHook(
key, scancode, action, character, extended, was_down,
[&, callback = std::move(callback)](bool handled) {
if (!handled) {
text_input_plugin_->KeyboardHook(key, scancode, action, character,
extended, was_down);
}
callback(handled);
});
}

void FlutterWindowsView::SendComposeBegin() {
Expand Down
Loading