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

Fix dead key crashes on Win32 #28047

Merged
merged 4 commits into from
Aug 13, 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
12 changes: 12 additions & 0 deletions shell/platform/windows/keyboard_key_embedder_handler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ char _GetBit(char32_t ch, size_t start, size_t end) {
return (ch >> end) & ((1 << (start - end)) - 1);
}

// Revert the "character" for a dead key to its normal value.
//
// When a dead key is pressed, the WM_KEYDOWN's lParam is mapped to a special
// value: the "normal character" | 0x80000000. For example, when pressing
// "dead key caret" (one that makes the following e into ê), its mapped
// character is 0x8000005E. "Reverting" it gives 0x5E, which is character '^'.
uint32_t _UndeadChar(uint32_t ch) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Love the name. :-)

return ch & ~0x80000000;
}

std::string ConvertChar32ToUtf8(char32_t ch) {
std::string result;
assert(0 <= ch && ch <= 0x10FFFF);
Expand Down Expand Up @@ -171,6 +181,8 @@ void KeyboardKeyEmbedderHandler::KeyboardHook(
bool next_has_record = true;
char character_bytes[kCharacterCacheSize];

character = _UndeadChar(character);

if (is_physical_down) {
if (had_record) {
if (was_down) {
Expand Down
43 changes: 37 additions & 6 deletions shell/platform/windows/keyboard_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,30 @@ static LPARAM CreateKeyEventLparam(USHORT scancode,
(LPARAM(scancode) << 16) | LPARAM(repeat_count));
}

typedef uint32_t (*MapVkToCharHandler)(uint32_t virtual_key);

uint32_t LayoutDefault(uint32_t virtual_key) {
return MapVirtualKey(virtual_key, MAPVK_VK_TO_CHAR);
}

uint32_t LayoutFrench(uint32_t virtual_key) {
switch (virtual_key) {
case 0xDD:
return 0x8000005E;
default:
return MapVirtualKey(virtual_key, MAPVK_VK_TO_CHAR);
}
}

class MockFlutterWindowWin32 : public FlutterWindowWin32,
public MockMessageQueue {
public:
typedef std::function<void(const std::u16string& text)> U16StringHandler;

MockFlutterWindowWin32(U16StringHandler on_text)
: FlutterWindowWin32(800, 600), on_text_(std::move(on_text)) {
: FlutterWindowWin32(800, 600),
on_text_(std::move(on_text)),
map_vk_to_char_(LayoutDefault) {
ON_CALL(*this, GetDpiScale())
.WillByDefault(Return(this->FlutterWindowWin32::GetDpiScale()));
}
Expand Down Expand Up @@ -90,6 +107,12 @@ class MockFlutterWindowWin32 : public FlutterWindowWin32,
MOCK_METHOD0(IsVisible, bool());
MOCK_METHOD1(UpdateCursorRect, void(const Rect&));

void SetLayout(MapVkToCharHandler map_vk_to_char) {
map_vk_to_char_ =
map_vk_to_char == nullptr ? LayoutDefault : map_vk_to_char;
}

protected:
virtual BOOL Win32PeekMessage(LPMSG lpMsg,
HWND hWnd,
UINT wMsgFilterMin,
Expand All @@ -99,9 +122,15 @@ class MockFlutterWindowWin32 : public FlutterWindowWin32,
wMsgFilterMax, wRemoveMsg);
}

virtual uint32_t Win32MapVkToChar(uint32_t virtual_key) override {
return map_vk_to_char_(virtual_key);
}

private:
U16StringHandler on_text_;

MapVkToCharHandler map_vk_to_char_;

LRESULT Win32SendMessage(HWND hWnd,
UINT const message,
WPARAM const wparam,
Expand Down Expand Up @@ -248,6 +277,8 @@ class KeyboardTester {

void Responding(bool response) { test_response = response; }

void SetLayout(MapVkToCharHandler layout) { window_->SetLayout(layout); }

void InjectMessages(int count, Win32Message message1, ...) {
Win32Message messages[count];
messages[0] = message1;
Expand Down Expand Up @@ -636,7 +667,7 @@ TEST(KeyboardTest, Digit1OnFrenchLayout) {
KeyboardTester tester;
tester.Responding(false);

// French Keyboard layout
tester.SetLayout(LayoutFrench);

// Press 1
tester.InjectMessages(
Expand Down Expand Up @@ -755,7 +786,7 @@ TEST(KeyboardTest, DeadKeyThatCombines) {
KeyboardTester tester;
tester.Responding(false);

// French Keyboard layout
tester.SetLayout(LayoutFrench);

// Press ^¨ (US: Left bracket)
tester.InjectMessages(
Expand All @@ -767,7 +798,7 @@ TEST(KeyboardTest, DeadKeyThatCombines) {

EXPECT_EQ(key_calls.size(), 1);
EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeDown,
kPhysicalBracketLeft, kLogicalBracketRight, "]",
kPhysicalBracketLeft, kLogicalBracketRight, "^",
kNotSynthesized);
clear_key_calls();

Expand Down Expand Up @@ -829,7 +860,7 @@ TEST(KeyboardTest, DeadKeyThatDoesNotCombine) {
KeyboardTester tester;
tester.Responding(false);

// French Keyboard layout
tester.SetLayout(LayoutFrench);

// Press ^¨ (US: Left bracket)
tester.InjectMessages(
Expand All @@ -841,7 +872,7 @@ TEST(KeyboardTest, DeadKeyThatDoesNotCombine) {

EXPECT_EQ(key_calls.size(), 1);
EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeDown,
kPhysicalBracketLeft, kLogicalBracketRight, "]",
kPhysicalBracketLeft, kLogicalBracketRight, "^",
kNotSynthesized);
clear_key_calls();

Expand Down
8 changes: 6 additions & 2 deletions shell/platform/windows/window_win32.cc
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ WindowWin32::HandleMessage(UINT const message,
// https://docs.microsoft.com/en-us/windows/win32/learnwin32/accelerator-tables
const char32_t event_character =
(message == WM_DEADCHAR || message == WM_SYSDEADCHAR)
? MapVirtualKey(keycode_for_char_message_, MAPVK_VK_TO_CHAR)
? Win32MapVkToChar(keycode_for_char_message_)
: IsPrintable(code_point) ? code_point
: 0;
bool handled = OnKey(keycode_for_char_message_, scancode, WM_KEYDOWN,
Expand Down Expand Up @@ -429,7 +429,7 @@ WindowWin32::HandleMessage(UINT const message,
// character messages. This allows key combinations such as "CTRL + Digit"
// to properly produce key down events even though `MapVirtualKey` returns
// a valid character. See https://github.com/flutter/flutter/issues/85587.
unsigned int character = MapVirtualKey(wparam, MAPVK_VK_TO_CHAR);
unsigned int character = Win32MapVkToChar(wparam);
UINT next_key_message = PeekNextMessageType(WM_KEYFIRST, WM_KEYLAST);
bool has_wm_char =
(next_key_message == WM_DEADCHAR ||
Expand Down Expand Up @@ -516,4 +516,8 @@ BOOL WindowWin32::Win32PeekMessage(LPMSG lpMsg,
return PeekMessage(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg);
}

uint32_t WindowWin32::Win32MapVkToChar(uint32_t virtual_key) {
return MapVirtualKey(virtual_key, MAPVK_VK_TO_CHAR);
}

} // namespace flutter
5 changes: 5 additions & 0 deletions shell/platform/windows/window_win32.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,11 @@ class WindowWin32 {
UINT wMsgFilterMax,
UINT wRemoveMsg);

// Win32's MapVirtualKey(*, MAPVK_VK_TO_CHAR).
//
// Used to process key messages. Exposed for dependency injection.
virtual uint32_t Win32MapVkToChar(uint32_t virtual_key);

private:
// Release OS resources associated with window.
void Destroy();
Expand Down