@@ -93,7 +93,7 @@ static bool IsKeyDownShiftRight(int virtual_key, bool was_down) {
93
93
}
94
94
95
95
// 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) {
97
97
return (ch & kDeadKeyCharMask ) != 0 ;
98
98
}
99
99
@@ -181,49 +181,33 @@ bool KeyboardManagerWin32::RemoveRedispatchedEvent(
181
181
const PendingEvent& incoming) {
182
182
for (auto iter = pending_redispatches_.begin ();
183
183
iter != pending_redispatches_.end (); ++iter) {
184
- if ((*iter)->hash == incoming.hash ) {
184
+ if ((*iter)->Hash () == incoming.Hash () ) {
185
185
pending_redispatches_.erase (iter);
186
186
return true ;
187
187
}
188
188
}
189
189
return false ;
190
190
}
191
191
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,
198
193
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)) {
211
195
return false ;
212
196
}
213
197
214
- if (IsKeyDownAltRight (action, key, extended)) {
198
+ if (IsKeyDownAltRight (event-> action , event-> key , event-> extended )) {
215
199
if (last_key_is_ctrl_left_down) {
216
200
should_synthesize_ctrl_left_up = true ;
217
201
}
218
202
}
219
- if (IsKeyDownCtrlLeft (action, key)) {
203
+ if (IsKeyDownCtrlLeft (event-> action , event-> key )) {
220
204
last_key_is_ctrl_left_down = true ;
221
- ctrl_left_scancode = scancode;
205
+ ctrl_left_scancode = event-> scancode ;
222
206
should_synthesize_ctrl_left_up = false ;
223
207
} else {
224
208
last_key_is_ctrl_left_down = false ;
225
209
}
226
- if (IsKeyUpAltRight (action, key, extended)) {
210
+ if (IsKeyUpAltRight (event-> action , event-> key , event-> extended )) {
227
211
if (should_synthesize_ctrl_left_up) {
228
212
should_synthesize_ctrl_left_up = false ;
229
213
PendingEvent ctrl_left_up{
@@ -236,8 +220,10 @@ bool KeyboardManagerWin32::OnKey(int key,
236
220
}
237
221
}
238
222
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 (),
241
227
callback = std::move (callback)](bool handled) {
242
228
callback (std::unique_ptr<PendingEvent>(event),
243
229
handled);
@@ -261,7 +247,7 @@ void KeyboardManagerWin32::HandleOnKeyResult(
261
247
// |SendInput|.
262
248
const bool is_syskey =
263
249
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 ) ||
265
251
is_syskey ||
266
252
IsKeyDownShiftRight (event->key , event->was_down );
267
253
@@ -284,93 +270,101 @@ void KeyboardManagerWin32::HandleOnKeyResult(
284
270
RedispatchEvent (std::move (event));
285
271
}
286
272
287
- bool KeyboardManagerWin32::HandleMessage (UINT const message ,
273
+ bool KeyboardManagerWin32::HandleMessage (UINT const action ,
288
274
WPARAM const wparam,
289
275
LPARAM const lparam) {
290
- switch (message ) {
276
+ switch (action ) {
291
277
case WM_DEADCHAR:
292
278
case WM_SYSDEADCHAR:
293
279
case WM_CHAR:
294
280
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);
296
284
297
- wchar_t character = static_cast <wchar_t >(wparam);
298
285
std::u16string text;
299
286
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.
303
290
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 ));
308
303
code_point =
309
- CodePointFromSurrogatePair (s_pending_high_surrogate, character);
310
- s_pending_high_surrogate = 0 ;
304
+ CodePointFromSurrogatePair (last_message->wparam , message.wparam );
311
305
} 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 );
314
310
}
315
311
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 ;
328
320
const bool extended = ((lparam >> 24 ) & 0x01 ) == 0x01 ;
329
321
const bool was_down = lparam & 0x40000000 ;
330
322
// Certain key combinations yield control characters as WM_CHAR's
331
323
// lParam. For example, 0x01 for Ctrl-A. Filter these characters. See
332
324
// 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) {
335
327
// Mask the resulting char with kDeadKeyCharMask anyway, because in
336
328
// rare cases the bit is *not* set (US INTL Shift-6 circumflex, see
337
329
// 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 ;
341
332
} 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 ;
354
334
}
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;
357
352
// For system characters, always pass them to the default WndProc so
358
353
// 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;
363
355
}
364
356
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)) {
374
368
window_delegate_->OnText (text);
375
369
}
376
370
return true ;
@@ -380,8 +374,11 @@ bool KeyboardManagerWin32::HandleMessage(UINT const message,
380
374
case WM_SYSKEYDOWN:
381
375
case WM_KEYUP:
382
376
case WM_SYSKEYUP: {
377
+ current_session_.clear ();
378
+ current_session_.push_back (
379
+ Win32Message{.action = action, .wparam = wparam, .lparam = lparam});
383
380
const bool is_keydown_message =
384
- (message == WM_KEYDOWN || message == WM_SYSKEYDOWN);
381
+ (action == WM_KEYDOWN || action == WM_SYSKEYDOWN);
385
382
// Check if this key produces a character. If so, the key press should
386
383
// be sent with the character produced at WM_CHAR. Store the produced
387
384
// keycode (it's not accessible from WM_CHAR) to be used in WM_CHAR.
@@ -397,30 +394,36 @@ bool KeyboardManagerWin32::HandleMessage(UINT const message,
397
394
next_key_message == WM_SYSDEADCHAR || next_key_message == WM_CHAR ||
398
395
next_key_message == WM_SYSCHAR);
399
396
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.
401
400
return true ;
402
401
}
403
- unsigned int keyCode (wparam);
402
+
403
+ // Resolve session: A non-char key event.
404
404
const uint8_t scancode = (lparam >> 16 ) & 0xff ;
405
405
const bool extended = ((lparam >> 24 ) & 0x01 ) == 0x01 ;
406
406
// 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);
408
408
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),
412
420
[this ](std::unique_ptr<PendingEvent> event, bool handled) {
413
421
HandleOnKeyResult (std::move (event), handled, 0 , std::u16string ());
414
422
});
415
- if (!is_new_event) {
416
- break ;
417
- }
423
+ const bool is_syskey = action == WM_SYSKEYDOWN || action == WM_SYSKEYUP;
418
424
// For system keys, always pass them to the default WndProc so that keys
419
425
// 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;
424
427
}
425
428
default :
426
429
assert (false );
0 commit comments