Flutter Windows Embedder
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

Constructor & Destructor Documentation

◆ KeyboardManager()

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

Definition at line 103 of file keyboard_manager.cc.

104  : window_delegate_(delegate),
105  last_key_is_ctrl_left_down(false),
106  should_synthesize_ctrl_left_up(false),
107  processing_event_(false) {}

Member Function Documentation

◆ HandleMessage()

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

Definition at line 144 of file keyboard_manager.cc.

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

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)
protectedvirtual

Definition at line 109 of file keyboard_manager.cc.

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

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:
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