| Flutter Windows Embedder |

Classes | Public Types | Public Member Functions | Protected Member Functions | List of all members
flutter::KeyboardManager Class Reference

`#include <keyboard_manager.h>`

## Classes
struct | PendingEvent
struct | Win32Message
class | WindowDelegate

## Public Types
using | KeyEventCallback = WindowDelegate::KeyEventCallback

## Public Member Functions
| KeyboardManager (WindowDelegate *delegate)
bool | HandleMessage (UINT const message, WPARAM const wparam, LPARAM const lparam)

## Protected Member Functions
virtual void | RedispatchEvent (std::unique_ptr< PendingEvent > event)

## Detailed Description

Definition at line 46 of file keyboard_manager.h.

## Member Typedef Documentation

## ◆ KeyEventCallback

using flutter::KeyboardManager::KeyEventCallback = WindowDelegate::KeyEventCallback

Definition at line 93 of file keyboard_manager.h.

## Constructor & Destructor Documentation

## ◆ KeyboardManager()

flutter::KeyboardManager::KeyboardManager | ( | WindowDelegate * | delegate | ) |
| explicit

Definition at line 105 of file keyboard_manager.cc.

106 : window_delegate_(delegate),
107 last_key_is_ctrl_left_down(false),
108 should_synthesize_ctrl_left_up(false),
109 processing_event_(false) {}

## Member Function Documentation

## ◆ HandleMessage()

bool flutter::KeyboardManager::HandleMessage | ( | UINT const | message,
|  | WPARAM const | wparam,
|  | LPARAM const | lparam
| ) |  |

Definition at line 146 of file keyboard_manager.cc.

148 {
149 if (RemoveRedispatchedMessage(action, wparam, lparam)) {
150 return false;
151 }
152 switch (action) {
153 case WM_DEADCHAR:
154 case WM_SYSDEADCHAR:
155 case WM_CHAR:
156 case WM_SYSCHAR: {
157 const Win32Message message =
158 Win32Message{.action = action, .wparam = wparam, .lparam = lparam};
159 current_session_.push_back(message);
160
161 char32_t code_point;
162 if (message.IsHighSurrogate()) {
163 // A high surrogate is always followed by a low surrogate. Process the
164 // session later and consider this message as handled.
165 return true;
166 } else if (message.IsLowSurrogate()) {
167 const Win32Message* last_message =
168 current_session_.size() <= 1
169 ? nullptr
170 : &current_session_[current_session_.size() - 2];
171 if (last_message == nullptr || !last_message->IsHighSurrogate()) {
172 return false;
173 }
174 // A low surrogate always follows a high surrogate, marking the end of
175 // a char session. Process the session after the if clause.
176 code_point =
177 CodePointFromSurrogatePair(last_message->wparam, message.wparam);
178 } else {
179 // A non-surrogate character always appears alone. Process the session
180 // after the if clause.
181 code_point = static_cast<wchar_t>(message.wparam);
182 }
183
184 // If this char message is preceded by a key down message, then dispatch
185 // the key down message as a key down event first, and only dispatch the
186 // OnText if the key down event is not handled.
187 if (current_session_.front().IsGeneralKeyDown()) {
188 const Win32Message first_message = current_session_.front();
189 const uint8_t scancode = (lparam >> 16) & 0xff;
190 const uint16_t key_code = first_message.wparam;
191 const bool extended = ((lparam >> 24) & 0x01) == 0x01;
192 const bool was_down = lparam & 0x40000000;
193 // Certain key combinations yield control characters as WM_CHAR's
194 // lParam. For example, 0x01 for Ctrl-A. Filter these characters. See
195 // https://docs.microsoft.com/en-us/windows/win32/learnwin32/accelerator-tables
196 char32_t character;
197 if (action == WM_DEADCHAR || action == WM_SYSDEADCHAR) {
198 // Mask the resulting char with kDeadKeyCharMask anyway, because in
199 // rare cases the bit is *not* set (US INTL Shift-6 circumflex, see
200 // https://github.com/flutter/flutter/issues/92654 .)
201 character =
202 window_delegate_->Win32MapVkToChar(key_code) | kDeadKeyCharMask;
203 } else {
204 character = IsPrintable(code_point) ? code_point : 0;
205 }
206 auto event = std::make_unique<PendingEvent>(PendingEvent{
207 .key = key_code,
208 .scancode = scancode,
209 .action = static_cast<UINT>(action == WM_SYSCHAR ? WM_SYSKEYDOWN
210 : WM_KEYDOWN),
211 .character = character,
212 .extended = extended,
213 .was_down = was_down,
214 .session = std::move(current_session_),
215 });
216
217 pending_events_.push_back(std::move(event));
218 ProcessNextEvent();
219
220 // SYS messages must not be consumed by `HandleMessage` so that they are
221 // forwarded to the system.
222 return !IsSysAction(action);
223 }
224
225 // If the charcter session is not preceded by a key down message,
226 // mark PendingEvent::action as WM_CHAR, informing |PerformProcessEvent|
227 // to dispatch the text content immediately.
228 //
229 // Only WM_CHAR should be treated as characters. WM_SYS*CHAR are not part
230 // of text input, and WM_DEADCHAR will be incorporated into a later
231 // WM_CHAR with the full character.
232 if (action == WM_CHAR) {
233 auto event = std::make_unique<PendingEvent>(PendingEvent{
234 .action = WM_CHAR,
235 .character = code_point,
236 .session = std::move(current_session_),
237 });
238 pending_events_.push_back(std::move(event));
239 ProcessNextEvent();
240 }
241 return true;
242 }
243
244 case WM_KEYDOWN:
245 case WM_SYSKEYDOWN:
246 case WM_KEYUP:
247 case WM_SYSKEYUP: {
248 if (wparam == VK_PACKET) {
249 return false;
250 }
251
252 const uint8_t scancode = (lparam >> 16) & 0xff;
253 const bool extended = ((lparam >> 24) & 0x01) == 0x01;
254 // If the key is a modifier, get its side.
255 const uint16_t key_code = ResolveKeyCode(wparam, extended, scancode);
256 const bool was_down = lparam & 0x40000000;
257
258 // Detect a pattern of key events in order to forge a CtrlLeft up event.
259 // See |IsKeyDownAltRight| for explanation.
260 if (IsKeyDownAltRight(action, key_code, extended)) {
261 if (last_key_is_ctrl_left_down) {
262 should_synthesize_ctrl_left_up = true;
263 }
264 }
265 if (IsKeyDownCtrlLeft(action, key_code)) {
266 last_key_is_ctrl_left_down = true;
267 ctrl_left_scancode = scancode;
268 should_synthesize_ctrl_left_up = false;
269 } else {
270 last_key_is_ctrl_left_down = false;
271 }
272 if (IsKeyUpAltRight(action, key_code, extended)) {
273 if (should_synthesize_ctrl_left_up) {
274 should_synthesize_ctrl_left_up = false;
275 const LPARAM lParam =
276 (1 /* repeat_count */ << 0) | (ctrl_left_scancode << 16) |
277 (0 /* extended */ << 24) | (1 /* prev_state */ << 30) |
278 (1 /* transition */ << 31);
279 window_delegate_->Win32DispatchMessage(WM_KEYUP, VK_CONTROL, lParam);
280 }
281 }
282
283 current_session_.clear();
284 current_session_.push_back(
285 Win32Message{.action = action, .wparam = wparam, .lparam = lparam});
286 const bool is_keydown_message =
287 (action == WM_KEYDOWN || action == WM_SYSKEYDOWN);
288 // Check if this key produces a character by peeking if this key down
289 // message has a following char message. Certain key messages are not
290 // followed by char messages even though `MapVirtualKey` returns a valid
291 // character (such as Ctrl + Digit, see
292 // https://github.com/flutter/flutter/issues/85587 ).
293 unsigned int character = window_delegate_->Win32MapVkToChar(wparam);
294 UINT next_key_action = PeekNextMessageType(WM_KEYFIRST, WM_KEYLAST);
295 bool has_char_action =
296 (next_key_action == WM_DEADCHAR ||
297 next_key_action == WM_SYSDEADCHAR || next_key_action == WM_CHAR ||
298 next_key_action == WM_SYSCHAR);
299 if (character > 0 && is_keydown_message && has_char_action) {
300 // This key down message has a following char message. Process this
301 // session in the char message, because the character for the key call
302 // should be decided by the char events. Consider this message as
303 // handled.
304 return true;
305 }
306
307 // This key down message is not followed by a char message. Conclude this
308 // session.
309 auto event = std::make_unique<PendingEvent>(PendingEvent{
310 .key = key_code,
311 .scancode = scancode,
312 .action = action,
313 .character = 0,
314 .extended = extended,
315 .was_down = was_down,
316 .session = std::move(current_session_),
317 });
318 pending_events_.push_back(std::move(event));
319 ProcessNextEvent();
320 // SYS messages must not be consumed by `HandleMessage` so that they are
321 // forwarded to the system.
322 return !IsSysAction(action);
323 }
324 default:
325 FML_LOG(FATAL) << "No event handler for keyboard event with action "
326 << action;
327 }
328 return false;
329 }

References flutter::KeyboardManager::Win32Message::action, action, flutter::KeyboardManager::PendingEvent::action, character, extended, flutter::KeyboardManager::Win32Message::IsHighSurrogate(), flutter::KeyboardManager::Win32Message::IsLowSurrogate(), flutter::kDeadKeyCharMask, flutter::KeyboardManager::PendingEvent::key, message, scancode, was_down, flutter::KeyboardManager::WindowDelegate::Win32DispatchMessage(), flutter::KeyboardManager::WindowDelegate::Win32MapVkToChar(), and flutter::KeyboardManager::Win32Message::wparam.

## ◆ RedispatchEvent()

void flutter::KeyboardManager::RedispatchEvent | ( | std::unique_ptr< PendingEvent > | event | ) |
| protected virtual

Definition at line 111 of file keyboard_manager.cc.

111 {
112 for (const Win32Message& message : event->session) {
113 // Never redispatch sys keys, because their original messages have been
114 // passed to the system default processor.
115 if (IsSysAction(message.action)) {
116 continue;
117 }
118 pending_redispatches_.push_back(message);
119 UINT result = window_delegate_->Win32DispatchMessage(
120 message.action, message.wparam, message.lparam);
121 if (result != 0) {
122 FML_LOG(ERROR) << "Unable to synthesize event for keyboard event.";
123 }
124 }
125 if (pending_redispatches_.size() > kMaxPendingEvents) {
126 FML_LOG(ERROR)
127 << "There are " << pending_redispatches_.size()
128 << " keyboard events that have not yet received a response from the "
129 << "framework. Are responses being sent?";
130 }
131 }

References flutter::KeyboardManager::Win32Message::action, flutter::KeyboardManager::Win32Message::lparam, message, flutter::KeyboardManager::WindowDelegate::Win32DispatchMessage(), and flutter::KeyboardManager::Win32Message::wparam.

The documentation for this class was generated from the following files:

- shell/platform/windows/keyboard_manager.h
- shell/platform/windows/keyboard_manager.cc

scancode
int scancode
Definition: keyboard_key_handler_unittests.cc:115
was_down
bool was_down
Definition: keyboard_key_handler_unittests.cc:119
extended
bool extended
Definition: keyboard_key_handler_unittests.cc:118
flutter::KeyboardManager::WindowDelegate::Win32MapVkToChar
virtual uint32_t Win32MapVkToChar(uint32_t virtual_key)=0
character
char32_t character
Definition: keyboard_key_handler_unittests.cc:117
flutter::KeyboardManager::Win32Message::lparam
LPARAM lparam
Definition: keyboard_manager.h:117
flutter::KeyboardManager::Win32Message::IsHighSurrogate
bool IsHighSurrogate() const
Definition: keyboard_manager.h:119
flutter::KeyboardManager::Win32Message::wparam
WPARAM wparam
Definition: keyboard_manager.h:116
message
Win32Message message
Definition: keyboard_unittests.cc:137
flutter::kDeadKeyCharMask
constexpr int kDeadKeyCharMask
Definition: keyboard_utils.h:30
action
int action
Definition: keyboard_key_handler_unittests.cc:116
flutter::KeyboardManager::Win32Message::action
UINT action
Definition: keyboard_manager.h:115
flutter::KeyboardManager::Win32Message::IsLowSurrogate
bool IsLowSurrogate() const
Definition: keyboard_manager.h:121
flutter::KeyboardManager::WindowDelegate::Win32DispatchMessage
virtual UINT Win32DispatchMessage(UINT Msg, WPARAM wParam, LPARAM lParam)=0