@@ -371,9 +371,7 @@ class KeyboardConverter {
371371 // followed by an immediate cancel event.
372372 (_shouldSynthesizeCapsLockUp () && event.code! == _kPhysicalCapsLock);
373373
374- final int ? lastLogicalRecord = _pressingRecords[physicalKey];
375-
376- ui.KeyEventType type;
374+ final ui.KeyEventType type;
377375
378376 if (_shouldSynthesizeCapsLockUp () && event.code! == _kPhysicalCapsLock) {
379377 // Case 1: Handle CapsLock on macOS
@@ -399,28 +397,45 @@ class KeyboardConverter {
399397
400398 } else if (isPhysicalDown) {
401399 // Case 2: Handle key down of normal keys
402- type = ui.KeyEventType .down;
403- if (lastLogicalRecord != null ) {
400+ if (_pressingRecords[physicalKey] != null ) {
404401 // This physical key is being pressed according to the record.
405402 if (event.repeat ?? false ) {
406403 // A normal repeated key.
407404 type = ui.KeyEventType .repeat;
408405 } else {
409406 // A non-repeated key has been pressed that has the exact physical key as
410- // a currently pressed one, usually indicating multiple keyboards are
411- // pressing keys with the same physical key, or the up event was lost
412- // during a loss of focus. The down event is ignored.
413- event.preventDefault ();
414- return ;
407+ // a currently pressed one. This can mean one of the following cases:
408+ //
409+ // * Multiple keyboards are pressing keys with the same physical key.
410+ // * The up event was lost during a loss of focus.
411+ // * The previous down event was a system shortcut and its release
412+ // was skipped (see `_startGuardingKey`,) such as holding Ctrl and
413+ // pressing V then V, within the "guard window".
414+ //
415+ // The three cases can't be distinguished, and in the 3rd case, the
416+ // latter event must be dispatched as down events for the framework to
417+ // correctly recognize and choose to not to handle. Therefore, an up
418+ // event is synthesized before it.
419+ _dispatchKeyData !(ui.KeyData (
420+ timeStamp: timeStamp,
421+ type: ui.KeyEventType .up,
422+ physical: physicalKey,
423+ logical: logicalKey,
424+ character: null ,
425+ synthesized: true ,
426+ ));
427+ _pressingRecords.remove (physicalKey);
428+ type = ui.KeyEventType .down;
415429 }
416430 } else {
417431 // This physical key is not being pressed according to the record. It's a
418432 // normal down event, whether the system event is a repeat or not.
433+ type = ui.KeyEventType .down;
419434 }
420435
421436 } else { // isPhysicalDown is false and not CapsLock
422437 // Case 2: Handle key up of normal keys
423- if (lastLogicalRecord == null ) {
438+ if (_pressingRecords[physicalKey] == null ) {
424439 // The physical key has been released before. It indicates multiple
425440 // keyboards pressed keys with the same physical key. Ignore the up event.
426441 event.preventDefault ();
@@ -430,6 +445,10 @@ class KeyboardConverter {
430445 type = ui.KeyEventType .up;
431446 }
432447
448+ // The _pressingRecords[physicalKey] might have been changed during the last
449+ // `if` clause.
450+ final int ? lastLogicalRecord = _pressingRecords[physicalKey];
451+
433452 final int ? nextLogicalRecord;
434453 switch (type) {
435454 case ui.KeyEventType .down:
0 commit comments