Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
91b9c61
Basic changes
dkwingsmt Aug 6, 2021
1cf7636
Fix build
dkwingsmt Aug 6, 2021
8a7d365
Revert some changes
dkwingsmt Aug 6, 2021
2997fb8
Fix tests (ATP)
dkwingsmt Aug 6, 2021
eac8a39
Win32Message and inject messagelist
dkwingsmt Aug 7, 2021
bf01779
wm builders
dkwingsmt Aug 7, 2021
aa9a1ef
Format
dkwingsmt Aug 7, 2021
1cca6fc
Correct
dkwingsmt Aug 7, 2021
84e61b7
LowerCaseAUnhandled
dkwingsmt Aug 9, 2021
770406f
ShiftLeftKeyA
dkwingsmt Aug 9, 2021
78499d5
Ctrl-A
dkwingsmt Aug 9, 2021
cc9577c
Ctrl-1
dkwingsmt Aug 9, 2021
b11134b
Digit1OnFrenchLayout
dkwingsmt Aug 9, 2021
68e1512
AltGrKeyQ
dkwingsmt Aug 9, 2021
32de55a
Dead key E
dkwingsmt Aug 10, 2021
b7a78c7
^1
dkwingsmt Aug 10, 2021
4432f98
Multibyte
dkwingsmt Aug 10, 2021
cb9ec7f
Merge remote-tracking branch 'upstream/master' into win-keyboard-unit…
dkwingsmt Aug 10, 2021
dbd8307
Format
dkwingsmt Aug 10, 2021
91d1c8e
License
dkwingsmt Aug 10, 2021
34b8fa2
Merge branch 'win-keyboard-unittests' of https://github.com/dkwingsmt…
dkwingsmt Aug 10, 2021
f67ed79
Revert key.dart
dkwingsmt Aug 10, 2021
a80350d
Move CreateKeyEventLparam
dkwingsmt Aug 11, 2021
c2ab9c2
MockEmbedderApiForKeyboard
dkwingsmt Aug 11, 2021
7674ac7
More MockEmbedderApiForKeyboard
dkwingsmt Aug 11, 2021
1efa671
Doc
dkwingsmt Aug 11, 2021
8a6bf16
Move MockMessageQueue
dkwingsmt Aug 11, 2021
d673a0b
is_deadchar doesnt seem needed
dkwingsmt Aug 11, 2021
8d84ec0
ConvertChar32ToUtf8
dkwingsmt Aug 11, 2021
0a832b0
PeekNextMessageType
dkwingsmt Aug 11, 2021
b8ff16e
Format
dkwingsmt Aug 11, 2021
357ff43
Merge remote-tracking branch 'upstream/master' into win-keyboard-unit…
dkwingsmt Aug 11, 2021
410947a
Apply suggestions from code review
dkwingsmt Aug 11, 2021
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
1 change: 1 addition & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -1658,6 +1658,7 @@ FILE: ../../../flutter/shell/platform/windows/keyboard_key_embedder_handler_unit
FILE: ../../../flutter/shell/platform/windows/keyboard_key_handler.cc
FILE: ../../../flutter/shell/platform/windows/keyboard_key_handler.h
FILE: ../../../flutter/shell/platform/windows/keyboard_key_handler_unittests.cc
FILE: ../../../flutter/shell/platform/windows/keyboard_unittests.cc
FILE: ../../../flutter/shell/platform/windows/platform_handler.cc
FILE: ../../../flutter/shell/platform/windows/platform_handler.h
FILE: ../../../flutter/shell/platform/windows/platform_handler_unittests.cc
Expand Down
6 changes: 6 additions & 0 deletions shell/platform/windows/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -243,12 +243,18 @@ executable("flutter_windows_unittests") {
"keyboard_key_channel_handler_unittests.cc",
"keyboard_key_embedder_handler_unittests.cc",
"keyboard_key_handler_unittests.cc",
"keyboard_unittests.cc",
"testing/flutter_window_win32_test.cc",
"testing/flutter_window_win32_test.h",
"testing/mock_window_binding_handler.cc",
"testing/mock_window_binding_handler.h",
"testing/mock_window_win32.cc",
"testing/mock_window_win32.h",
"testing/test_keyboard.cc",
"testing/test_keyboard.h",
"testing/test_keyboard_unittests.cc",
"testing/wm_builders.cc",
"testing/wm_builders.h",
"text_input_plugin_unittest.cc",
"window_proc_delegate_manager_win32_unittests.cc",
"window_win32_unittests.cc",
Expand Down
192 changes: 81 additions & 111 deletions shell/platform/windows/flutter_window_win32_unittests.cc

Large diffs are not rendered by default.

27 changes: 15 additions & 12 deletions shell/platform/windows/flutter_windows_view.cc
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,17 @@ void FlutterWindowsView::SetEngine(

// Set up the system channel handlers.
auto internal_plugin_messenger = internal_plugin_registrar_->messenger();
RegisterKeyboardHandlers(internal_plugin_messenger);
#ifdef WINUWP
flutter::KeyboardKeyHandler::EventDispatcher dispatch_event = nullptr;
flutter::KeyboardKeyEmbedderHandler::GetKeyStateHandler get_key_state =
nullptr;
#else
flutter::KeyboardKeyHandler::EventDispatcher dispatch_event = SendInput;
flutter::KeyboardKeyEmbedderHandler::GetKeyStateHandler get_key_state =
GetKeyState;
#endif
RegisterKeyboardHandlers(internal_plugin_messenger, dispatch_event,
get_key_state);
platform_handler_ = PlatformHandler::Create(internal_plugin_messenger, this);
cursor_handler_ = std::make_unique<flutter::CursorHandler>(
internal_plugin_messenger, binding_handler_.get());
Expand All @@ -67,7 +77,9 @@ void FlutterWindowsView::SetEngine(
}

void FlutterWindowsView::RegisterKeyboardHandlers(
flutter::BinaryMessenger* messenger) {
flutter::BinaryMessenger* messenger,
flutter::KeyboardKeyHandler::EventDispatcher dispatch_event,
flutter::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.)
Expand All @@ -76,17 +88,8 @@ void FlutterWindowsView::RegisterKeyboardHandlers(
// 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|.
#ifdef WINUWP
flutter::KeyboardKeyHandler::EventDispatcher redispatch_event = nullptr;
flutter::KeyboardKeyEmbedderHandler::GetKeyStateHandler get_key_state =
nullptr;
#else
flutter::KeyboardKeyHandler::EventDispatcher redispatch_event = SendInput;
flutter::KeyboardKeyEmbedderHandler::GetKeyStateHandler get_key_state =
GetKeyState;
#endif
auto key_handler =
std::make_unique<flutter::KeyboardKeyHandler>(redispatch_event);
std::make_unique<flutter::KeyboardKeyHandler>(dispatch_event);
key_handler->AddDelegate(std::make_unique<KeyboardKeyEmbedderHandler>(
[this](const FlutterKeyEvent& event, FlutterKeyEventCallback callback,
void* user_data) {
Expand Down
12 changes: 11 additions & 1 deletion shell/platform/windows/flutter_windows_view.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
#include "flutter/shell/platform/windows/cursor_handler.h"
#include "flutter/shell/platform/windows/flutter_windows_engine.h"
#include "flutter/shell/platform/windows/keyboard_handler_base.h"
#include "flutter/shell/platform/windows/keyboard_key_embedder_handler.h"
#include "flutter/shell/platform/windows/keyboard_key_handler.h"
#include "flutter/shell/platform/windows/platform_handler.h"
#include "flutter/shell/platform/windows/public/flutter_windows.h"
#include "flutter/shell/platform/windows/text_input_plugin_delegate.h"
Expand Down Expand Up @@ -144,7 +146,15 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate,

protected:
// Called to create the keyboard hook handlers.
virtual void RegisterKeyboardHandlers(flutter::BinaryMessenger* messenger);
//
// The provided |dispatch_event| is where to inject events into the system,
// while |get_key_state| is where to acquire keyboard states. They will be
// the system APIs in production classes, but might be replaced with mock
// functions in unit tests.
virtual void RegisterKeyboardHandlers(
flutter::BinaryMessenger* messenger,
flutter::KeyboardKeyHandler::EventDispatcher dispatch_event,
flutter::KeyboardKeyEmbedderHandler::GetKeyStateHandler get_key_state);

// Used by RegisterKeyboardHandlers to add a new keyboard hook handler.
void AddKeyboardHandler(
Expand Down
85 changes: 10 additions & 75 deletions shell/platform/windows/flutter_windows_view_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "flutter/shell/platform/windows/flutter_windows_view.h"
#include "flutter/shell/platform/windows/testing/engine_modifier.h"
#include "flutter/shell/platform/windows/testing/mock_window_binding_handler.h"
#include "flutter/shell/platform/windows/testing/test_keyboard.h"

#include "gmock/gmock.h"
#include "gtest/gtest.h"
Expand Down Expand Up @@ -59,82 +60,16 @@ std::unique_ptr<FlutterWindowsEngine> GetTestEngine() {
auto engine = std::make_unique<FlutterWindowsEngine>(project);

EngineModifier modifier(engine.get());

// This mock handles channel messages. This mock handles key events sent
// through the message channel is recorded in `key_event_logs`.
modifier.embedder_api().SendPlatformMessage =
[](FLUTTER_API_SYMBOL(FlutterEngine) engine,
const FlutterPlatformMessage* message) {
if (std::string(message->channel) == std::string("flutter/settings")) {
return kSuccess;
}
if (std::string(message->channel) == std::string("flutter/keyevent")) {
key_event_logs.push_back(kKeyEventFromChannel);
auto response = keyHandlingResponse(true);
const TestResponseHandle* response_handle =
reinterpret_cast<const TestResponseHandle*>(
message->response_handle);
if (response_handle->callback != nullptr) {
response_handle->callback(response->data(), response->size(),
response_handle->user_data);
}
return kSuccess;
}
return kSuccess;
};

// This mock handles key events sent through the embedder API,
// and records it in `key_event_logs`.
modifier.embedder_api().SendKeyEvent =
[](FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterKeyEvent* event,
FlutterKeyEventCallback callback, void* user_data) {
MockEmbedderApiForKeyboard(
modifier,
[] {
key_event_logs.push_back(kKeyEventFromChannel);
return test_response;
},
[](const FlutterKeyEvent* event) {
key_event_logs.push_back(kKeyEventFromEmbedder);
if (callback != nullptr) {
callback(test_response, user_data);
}
return kSuccess;
};

// The following mocks enable channel mocking.
modifier.embedder_api().PlatformMessageCreateResponseHandle =
[](auto engine, auto data_callback, auto user_data, auto response_out) {
TestResponseHandle* response_handle = new TestResponseHandle();
response_handle->user_data = user_data;
response_handle->callback = data_callback;
*response_out = reinterpret_cast<FlutterPlatformMessageResponseHandle*>(
response_handle);
return kSuccess;
};

modifier.embedder_api().PlatformMessageReleaseResponseHandle =
[](FLUTTER_API_SYMBOL(FlutterEngine) engine,
FlutterPlatformMessageResponseHandle* response) {
const TestResponseHandle* response_handle =
reinterpret_cast<const TestResponseHandle*>(response);
delete response_handle;
return kSuccess;
};

// The following mocks allows RunWithEntrypoint to be run, which creates a
// non-empty FlutterEngine and enables SendKeyEvent.

modifier.embedder_api().Run =
[](size_t version, const FlutterRendererConfig* config,
const FlutterProjectArgs* args, void* user_data,
FLUTTER_API_SYMBOL(FlutterEngine) * engine_out) {
*engine_out = reinterpret_cast<FLUTTER_API_SYMBOL(FlutterEngine)>(1);

return kSuccess;
};
modifier.embedder_api().UpdateLocales =
[](auto engine, const FlutterLocale** locales, size_t locales_count) {
return kSuccess;
};
modifier.embedder_api().SendWindowMetricsEvent =
[](auto engine, const FlutterWindowMetricsEvent* event) {
return kSuccess;
};
modifier.embedder_api().Shutdown = [](auto engine) { return kSuccess; };
return test_response;
});

engine->RunWithEntrypoint(nullptr);
return engine;
Expand Down
38 changes: 33 additions & 5 deletions shell/platform/windows/keyboard_key_embedder_handler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,35 @@ constexpr SHORT kStateMaskPressed = 0x80;
const char* empty_character = "";
} // namespace

// Get some bits of the char, from the start'th bit from the right (excluded)
// to the end'th bit from the right (included).
//
// For example, _GetBit(0x1234, 8, 4) => 0x3.
char _GetBit(char32_t ch, size_t start, size_t end) {
return (ch >> end) & ((1 << (start - end)) - 1);
}

std::string ConvertChar32ToUtf8(char32_t ch) {
std::string result;
assert(0 <= ch && ch <= 0x10FFFF);
if (ch <= 0x007F) {
result.push_back(ch);
} else if (ch <= 0x07FF) {
result.push_back(0b11000000 + _GetBit(ch, 11, 6));
result.push_back(0b10000000 + _GetBit(ch, 6, 0));
} else if (ch <= 0xFFFF) {
result.push_back(0b11100000 + _GetBit(ch, 16, 12));
result.push_back(0b10000000 + _GetBit(ch, 12, 6));
result.push_back(0b10000000 + _GetBit(ch, 6, 0));
} else {
result.push_back(0b11110000 + _GetBit(ch, 21, 18));
result.push_back(0b10000000 + _GetBit(ch, 18, 12));
result.push_back(0b10000000 + _GetBit(ch, 12, 6));
result.push_back(0b10000000 + _GetBit(ch, 6, 0));
}
return result;
}

KeyboardKeyEmbedderHandler::KeyboardKeyEmbedderHandler(
SendEvent send_event,
GetKeyStateHandler get_key_state)
Expand Down Expand Up @@ -267,10 +296,10 @@ void KeyboardKeyEmbedderHandler::SynchronizeCritialToggledStates(
continue;
}
assert(key_info.logical_key != 0);
SHORT state = get_key_state_(virtual_key);

// Check toggling state first, because it might alter pressing state.
if (key_info.check_toggled) {
SHORT state = get_key_state_(virtual_key);
bool should_toggled = state & kStateMaskToggled;
if (virtual_key == toggle_virtual_key) {
key_info.toggled_on = !key_info.toggled_on;
Expand Down Expand Up @@ -312,8 +341,8 @@ void KeyboardKeyEmbedderHandler::SynchronizeCritialPressedStates() {
continue;
}
assert(key_info.logical_key != 0);
SHORT state = get_key_state_(virtual_key);
if (key_info.check_pressed) {
SHORT state = get_key_state_(virtual_key);
auto recorded_pressed_iter = pressingRecords_.find(key_info.physical_key);
bool recorded_pressed = recorded_pressed_iter != pressingRecords_.end();
bool should_pressed = state & kStateMaskPressed;
Expand Down Expand Up @@ -388,9 +417,8 @@ void KeyboardKeyEmbedderHandler::ConvertUtf32ToUtf8_(char* out, char32_t ch) {
out[0] = '\0';
return;
}
// TODO: Correctly handle UTF-32
std::wstring text({static_cast<wchar_t>(ch)});
strcpy_s(out, kCharacterCacheSize, Utf8FromUtf16(text).c_str());
std::string result = ConvertChar32ToUtf8(ch);
strcpy_s(out, kCharacterCacheSize, result.c_str());
}

FlutterKeyEvent KeyboardKeyEmbedderHandler::CreateEmptyEvent() {
Expand Down
2 changes: 1 addition & 1 deletion shell/platform/windows/keyboard_key_embedder_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

namespace flutter {

namespace {} // namespace
std::string ConvertChar32ToUtf8(char32_t ch);

// A delegate of |KeyboardKeyHandler| that handles events by sending
// converted |FlutterKeyEvent|s through the embedder API.
Expand Down
26 changes: 26 additions & 0 deletions shell/platform/windows/keyboard_key_embedder_handler_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,32 @@ constexpr uint64_t kVirtualKeyA = 0x41;
using namespace ::flutter::testing::keycodes;
} // namespace

TEST(KeyboardKeyEmbedderHandlerTest, ConvertChar32ToUtf8) {
std::string result;

result = ConvertChar32ToUtf8(0x0024);
EXPECT_EQ(result.length(), 1);
EXPECT_EQ(result[0], '\x24');

result = ConvertChar32ToUtf8(0x00A2);
EXPECT_EQ(result.length(), 2);
EXPECT_EQ(result[0], '\xC2');
EXPECT_EQ(result[1], '\xA2');

result = ConvertChar32ToUtf8(0x0939);
EXPECT_EQ(result.length(), 3);
EXPECT_EQ(result[0], '\xE0');
EXPECT_EQ(result[1], '\xA4');
EXPECT_EQ(result[2], '\xB9');

result = ConvertChar32ToUtf8(0x10348);
EXPECT_EQ(result.length(), 4);
EXPECT_EQ(result[0], '\xF0');
EXPECT_EQ(result[1], '\x90');
EXPECT_EQ(result[2], '\x8D');
EXPECT_EQ(result[3], '\x88');
}

// Test the most basic key events.
//
// Press, hold, and release key A on an US keyboard.
Expand Down
4 changes: 2 additions & 2 deletions shell/platform/windows/keyboard_key_handler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ static bool IsKeyDownAltRight(int action, int virtual_key, bool extended) {
#ifdef WINUWP
return false;
#else
return virtual_key == VK_LMENU && extended && action == WM_KEYDOWN;
return virtual_key == VK_RMENU && extended && action == WM_KEYDOWN;
#endif
}

Expand All @@ -73,7 +73,7 @@ static bool IsKeyUpAltRight(int action, int virtual_key, bool extended) {
#ifdef WINUWP
return false;
#else
return virtual_key == VK_LMENU && extended && action == WM_KEYUP;
return virtual_key == VK_RMENU && extended && action == WM_KEYUP;
#endif
}

Expand Down
Loading