@@ -93,7 +93,7 @@ static bool IsKeyDownShiftRight(int virtual_key, bool was_down) {
9393}
9494
9595// Returns if a character sent by Win32 is a dead key.
96- static bool _IsDeadKey (uint32_t ch) {
96+ static bool IsDeadKey (uint32_t ch) {
9797 return (ch & kDeadKeyCharMask ) != 0 ;
9898}
9999
@@ -181,49 +181,33 @@ bool KeyboardManagerWin32::RemoveRedispatchedEvent(
181181 const PendingEvent& incoming) {
182182 for (auto iter = pending_redispatches_.begin ();
183183 iter != pending_redispatches_.end (); ++iter) {
184- if ((*iter)->hash == incoming.hash ) {
184+ if ((*iter)->Hash () == incoming.Hash () ) {
185185 pending_redispatches_.erase (iter);
186186 return true ;
187187 }
188188 }
189189 return false ;
190190}
191191
192- bool KeyboardManagerWin32::OnKey (int key,
193- int scancode,
194- int action,
195- char32_t character,
196- bool extended,
197- bool was_down,
192+ bool KeyboardManagerWin32::OnKey (std::unique_ptr<PendingEvent> event,
198193 OnKeyCallback callback) {
199- std::unique_ptr<PendingEvent> incoming =
200- std::make_unique<PendingEvent>(PendingEvent{
201- .key = static_cast <uint32_t >(key),
202- .scancode = static_cast <uint8_t >(scancode),
203- .action = static_cast <uint32_t >(action),
204- .character = character,
205- .extended = extended,
206- .was_down = was_down,
207- });
208- incoming->hash = ComputeEventHash (*incoming);
209-
210- if (RemoveRedispatchedEvent (*incoming)) {
194+ if (RemoveRedispatchedEvent (*event)) {
211195 return false ;
212196 }
213197
214- if (IsKeyDownAltRight (action, key, extended)) {
198+ if (IsKeyDownAltRight (event-> action , event-> key , event-> extended )) {
215199 if (last_key_is_ctrl_left_down) {
216200 should_synthesize_ctrl_left_up = true ;
217201 }
218202 }
219- if (IsKeyDownCtrlLeft (action, key)) {
203+ if (IsKeyDownCtrlLeft (event-> action , event-> key )) {
220204 last_key_is_ctrl_left_down = true ;
221- ctrl_left_scancode = scancode;
205+ ctrl_left_scancode = event-> scancode ;
222206 should_synthesize_ctrl_left_up = false ;
223207 } else {
224208 last_key_is_ctrl_left_down = false ;
225209 }
226- if (IsKeyUpAltRight (action, key, extended)) {
210+ if (IsKeyUpAltRight (event-> action , event-> key , event-> extended )) {
227211 if (should_synthesize_ctrl_left_up) {
228212 should_synthesize_ctrl_left_up = false ;
229213 PendingEvent ctrl_left_up{
@@ -236,8 +220,10 @@ bool KeyboardManagerWin32::OnKey(int key,
236220 }
237221 }
238222
239- window_delegate_->OnKey (key, scancode, action, character, extended, was_down,
240- [this , event = incoming.release (),
223+ const PendingEvent clone = *event;
224+ window_delegate_->OnKey (clone.key , clone.scancode , clone.action ,
225+ clone.character , clone.extended , clone.was_down ,
226+ [this , event = event.release (),
241227 callback = std::move (callback)](bool handled) {
242228 callback (std::unique_ptr<PendingEvent>(event),
243229 handled);
@@ -261,7 +247,7 @@ void KeyboardManagerWin32::HandleOnKeyResult(
261247 // |SendInput|.
262248 const bool is_syskey =
263249 event->action == WM_SYSKEYDOWN || event->action == WM_SYSKEYUP;
264- const bool real_handled = handled || _IsDeadKey (event->character ) ||
250+ const bool real_handled = handled || IsDeadKey (event->character ) ||
265251 is_syskey ||
266252 IsKeyDownShiftRight (event->key , event->was_down );
267253
@@ -284,93 +270,101 @@ void KeyboardManagerWin32::HandleOnKeyResult(
284270 RedispatchEvent (std::move (event));
285271}
286272
287- bool KeyboardManagerWin32::HandleMessage (UINT const message ,
273+ bool KeyboardManagerWin32::HandleMessage (UINT const action ,
288274 WPARAM const wparam,
289275 LPARAM const lparam) {
290- switch (message ) {
276+ switch (action ) {
291277 case WM_DEADCHAR:
292278 case WM_SYSDEADCHAR:
293279 case WM_CHAR:
294280 case WM_SYSCHAR: {
295- static wchar_t s_pending_high_surrogate = 0 ;
281+ const Win32Message message =
282+ Win32Message{.action = action, .wparam = wparam, .lparam = lparam};
283+ current_session_.push_back (message);
296284
297- wchar_t character = static_cast <wchar_t >(wparam);
298285 std::u16string text;
299286 char32_t code_point;
300- if (IS_HIGH_SURROGATE (character )) {
301- // Save to send later with the trailing surrogate.
302- s_pending_high_surrogate = character;
287+ if (message. IsHighSurrogate ( )) {
288+ // A high surrogate is always followed by a low surrogate. Process the
289+ // session later and consider this message as handled.
303290 return true ;
304- } else if (IS_LOW_SURROGATE (character) && s_pending_high_surrogate != 0 ) {
305- text.push_back (s_pending_high_surrogate);
306- text.push_back (character);
307- // Merge the surrogate pairs for the key event.
291+ } else if (message.IsLowSurrogate ()) {
292+ const Win32Message* last_message =
293+ current_session_.size () <= 1
294+ ? nullptr
295+ : ¤t_session_[current_session_.size () - 2 ];
296+ if (last_message == nullptr || !last_message->IsHighSurrogate ()) {
297+ return false ;
298+ }
299+ // A low surrogate always follows a high surrogate, marking the end of
300+ // a char session. Process the session after the if clause.
301+ text.push_back (static_cast <wchar_t >(last_message->wparam ));
302+ text.push_back (static_cast <wchar_t >(message.wparam ));
308303 code_point =
309- CodePointFromSurrogatePair (s_pending_high_surrogate, character);
310- s_pending_high_surrogate = 0 ;
304+ CodePointFromSurrogatePair (last_message->wparam , message.wparam );
311305 } else {
312- text.push_back (character);
313- code_point = character;
306+ // A non-surrogate character always appears alone. Process the session
307+ // after the if clause.
308+ text.push_back (static_cast <wchar_t >(message.wparam ));
309+ code_point = static_cast <wchar_t >(message.wparam );
314310 }
315311
316- const unsigned int scancode = (lparam >> 16 ) & 0xff ;
317-
318- // All key presses that generate a character should be sent from
319- // WM_CHAR. In order to send the full key press information, the keycode
320- // is persisted in keycode_for_char_message_ obtained from WM_KEYDOWN.
321- //
322- // A high surrogate is always followed by a low surrogate, while a
323- // non-surrogate character always appears alone. Filter out high
324- // surrogates so that it's the low surrogate message that triggers
325- // the onKey, asks if the framework handles it (which can only be done
326- // once), and calls OnText during the redispatched messages.
327- if (keycode_for_char_message_ != 0 && !IS_HIGH_SURROGATE (character)) {
312+ // If this char message is preceded by a key down message, then dispatch
313+ // the key down message as a key down event first, and only dispatch the
314+ // OnText if the key down event is not handled.
315+ if (current_session_.front ().IsGeneralKeyDown ()) {
316+ const Win32Message first_message = current_session_.front ();
317+ current_session_.clear ();
318+ const uint8_t scancode = (lparam >> 16 ) & 0xff ;
319+ const uint16_t key_code = first_message.wparam ;
328320 const bool extended = ((lparam >> 24 ) & 0x01 ) == 0x01 ;
329321 const bool was_down = lparam & 0x40000000 ;
330322 // Certain key combinations yield control characters as WM_CHAR's
331323 // lParam. For example, 0x01 for Ctrl-A. Filter these characters. See
332324 // https://docs.microsoft.com/en-us/windows/win32/learnwin32/accelerator-tables
333- char32_t event_character ;
334- if (message == WM_DEADCHAR || message == WM_SYSDEADCHAR) {
325+ char32_t character ;
326+ if (action == WM_DEADCHAR || action == WM_SYSDEADCHAR) {
335327 // Mask the resulting char with kDeadKeyCharMask anyway, because in
336328 // rare cases the bit is *not* set (US INTL Shift-6 circumflex, see
337329 // https://github.com/flutter/flutter/issues/92654 .)
338- event_character =
339- window_delegate_->Win32MapVkToChar (keycode_for_char_message_) |
340- kDeadKeyCharMask ;
330+ character =
331+ window_delegate_->Win32MapVkToChar (key_code) | kDeadKeyCharMask ;
341332 } else {
342- event_character = IsPrintable (code_point) ? code_point : 0 ;
343- }
344- bool is_new_event =
345- OnKey (keycode_for_char_message_, scancode,
346- message == WM_SYSCHAR ? WM_SYSKEYDOWN : WM_KEYDOWN,
347- event_character, extended, was_down,
348- [this , message, text](std::unique_ptr<PendingEvent> event,
349- bool handled) {
350- HandleOnKeyResult (std::move (event), handled, message, text);
351- });
352- if (!is_new_event) {
353- break ;
333+ character = IsPrintable (code_point) ? code_point : 0 ;
354334 }
355- keycode_for_char_message_ = 0 ;
356-
335+ auto event = std::make_unique<PendingEvent>(PendingEvent{
336+ .key = key_code,
337+ .scancode = scancode,
338+ .action = static_cast <UINT>(action == WM_SYSCHAR ? WM_SYSKEYDOWN
339+ : WM_KEYDOWN),
340+ .character = character,
341+ .extended = extended,
342+ .was_down = was_down,
343+ .session = std::move (current_session_),
344+ });
345+ const bool is_unmet_event = OnKey (
346+ std::move (event),
347+ [this , char_action = action, text](
348+ std::unique_ptr<PendingEvent> event, bool handled) {
349+ HandleOnKeyResult (std::move (event), handled, char_action, text);
350+ });
351+ const bool is_syskey = action == WM_SYSCHAR;
357352 // For system characters, always pass them to the default WndProc so
358353 // that system keys like the ALT-TAB are processed correctly.
359- if (message == WM_SYSCHAR) {
360- break ;
361- }
362- return true ;
354+ return is_unmet_event && !is_syskey;
363355 }
364356
365- // Of the messages handled here, only WM_CHAR should be treated as
366- // characters. WM_SYS*CHAR are not part of text input, and WM_DEADCHAR
367- // will be incorporated into a later WM_CHAR with the full character.
368- // Also filter out:
369- // - Lead surrogates, which like dead keys will be send once combined.
370- // - ASCII control characters, which are sent as WM_CHAR events for all
371- // control key shortcuts.
372- if (message == WM_CHAR && s_pending_high_surrogate == 0 &&
373- IsPrintable (character)) {
357+ // If the charcter session is not preceded by a key down message, dispatch
358+ // the OnText immediately.
359+
360+ // Only WM_CHAR should be treated as characters. WM_SYS*CHAR are not part
361+ // of text input, and WM_DEADCHAR will be incorporated into a later
362+ // WM_CHAR with the full character.
363+ //
364+ // Also filter out ASCII control characters, which are sent as WM_CHAR
365+ // events for all control key shortcuts.
366+ current_session_.clear ();
367+ if (action == WM_CHAR && IsPrintable (wparam)) {
374368 window_delegate_->OnText (text);
375369 }
376370 return true ;
@@ -380,8 +374,11 @@ bool KeyboardManagerWin32::HandleMessage(UINT const message,
380374 case WM_SYSKEYDOWN:
381375 case WM_KEYUP:
382376 case WM_SYSKEYUP: {
377+ current_session_.clear ();
378+ current_session_.push_back (
379+ Win32Message{.action = action, .wparam = wparam, .lparam = lparam});
383380 const bool is_keydown_message =
384- (message == WM_KEYDOWN || message == WM_SYSKEYDOWN);
381+ (action == WM_KEYDOWN || action == WM_SYSKEYDOWN);
385382 // Check if this key produces a character. If so, the key press should
386383 // be sent with the character produced at WM_CHAR. Store the produced
387384 // keycode (it's not accessible from WM_CHAR) to be used in WM_CHAR.
@@ -397,30 +394,36 @@ bool KeyboardManagerWin32::HandleMessage(UINT const message,
397394 next_key_message == WM_SYSDEADCHAR || next_key_message == WM_CHAR ||
398395 next_key_message == WM_SYSCHAR);
399396 if (character > 0 && is_keydown_message && has_wm_char) {
400- keycode_for_char_message_ = wparam;
397+ // This key down message has following char events. Process later,
398+ // because the character for the OnKey should be decided by the char
399+ // events. Consider this event as handled.
401400 return true ;
402401 }
403- unsigned int keyCode (wparam);
402+
403+ // Resolve session: A non-char key event.
404404 const uint8_t scancode = (lparam >> 16 ) & 0xff ;
405405 const bool extended = ((lparam >> 24 ) & 0x01 ) == 0x01 ;
406406 // If the key is a modifier, get its side.
407- keyCode = ResolveKeyCode (keyCode , extended, scancode);
407+ const uint16_t key_code = ResolveKeyCode (wparam , extended, scancode);
408408 const bool was_down = lparam & 0x40000000 ;
409- bool is_syskey = message == WM_SYSKEYDOWN || message == WM_SYSKEYUP;
410- bool is_new_event = OnKey (
411- keyCode, scancode, message, 0 , extended, was_down,
409+ auto event = std::make_unique<PendingEvent>(PendingEvent{
410+ .key = key_code,
411+ .scancode = scancode,
412+ .action = action,
413+ .character = 0 ,
414+ .extended = extended,
415+ .was_down = was_down,
416+ .session = std::move (current_session_),
417+ });
418+ const bool is_unmet_event = OnKey (
419+ std::move (event),
412420 [this ](std::unique_ptr<PendingEvent> event, bool handled) {
413421 HandleOnKeyResult (std::move (event), handled, 0 , std::u16string ());
414422 });
415- if (!is_new_event) {
416- break ;
417- }
423+ const bool is_syskey = action == WM_SYSKEYDOWN || action == WM_SYSKEYUP;
418424 // For system keys, always pass them to the default WndProc so that keys
419425 // like the ALT-TAB or Kanji switches are processed correctly.
420- if (is_syskey) {
421- break ;
422- }
423- return true ;
426+ return is_unmet_event && !is_syskey;
424427 }
425428 default :
426429 assert (false );
0 commit comments