Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
Instance Methods | Properties | List of all members
FlutterEmbedderKeyResponder Class Reference

#include <FlutterEmbedderKeyResponder.h>

Inheritance diagram for FlutterEmbedderKeyResponder:
<FlutterKeyPrimaryResponder> <FlutterKeyPrimaryResponder>

Instance Methods

(nonnull instancetype) - initWithSendEvent:
 
(nonnull instancetype) - initWithSendEvent:
 
(void) - syncModifiersIfNeeded:timestamp:
 
(nonnull NSDictionary *) - getPressedState
 
(nonnull instancetype) - initWithSendEvent: [implementation]
 
(void) - handlePress:callback: [implementation]
 
(void) - synchronizeModifiers: [implementation]
 
(void) - synthesizeCapsLockTapWithTimestamp: [implementation]
 
(void) - updateKey:asPressed: [implementation]
 
(void) - sendPrimaryFlutterEvent:callback: [implementation]
 
(void) - sendEmptyEvent [implementation]
 
(void) - synthesizeModifierEventOfType:timestamp:keyCode: [implementation]
 
(void) - handlePressBegin:callback: [implementation]
 
(void) - handlePressEnd:callback: [implementation]
 
(void) - handleResponse:forId: [implementation]
 
(UInt32) - fixSidedFlags:withLeftFlag:withRightFlag:withLeftKey:withRightKey:withKeyCode:keyDown:forFlags: [implementation]
 
(UInt32) - adjustModifiers: [implementation]
 
(void) - handleEvent:callback: [implementation]
 
(void) - synchronizeModifiers:ignoringFlags:timestamp:guard: [implementation]
 
(void) - sendSynthesizedFlutterEvent:guard: [implementation]
 
(void) - sendCapsLockTapWithTimestamp:synthesizeDown:callback: [implementation]
 
(void) - sendModifierEventOfType:timestamp:keyCode:synthesized:callback: [implementation]
 
(void) - handleDownEvent:callback: [implementation]
 
(void) - handleUpEvent:callback: [implementation]
 
(void) - handleCapsLockEvent:callback: [implementation]
 
(void) - handleFlagEvent:callback: [implementation]
 
- Instance Methods inherited from <FlutterKeyPrimaryResponder>
(void) - handleEvent:callback:
 

Properties

FlutterSendKeyEvent sendEvent [implementation]
 
FlutterSendEmbedderKeyEvent sendEvent [implementation]
 
NSMutableDictionary< NSNumber *, NSNumber * > * pressingRecords [implementation]
 
NSUInteger modifierFlagOfInterestMask [implementation]
 
NSUInteger lastModifierFlagsOfInterest [implementation]
 
uint64_t responseId [implementation]
 
NSMutableDictionary< NSNumber *, FlutterAsyncKeyCallback > * pendingResponses [implementation]
 
- Properties inherited from <FlutterKeyPrimaryResponder>
NSMutableDictionary< NSNumber *, NSNumber * > * layoutMap
 

Detailed Description

A primary responder of |FlutterKeyboardManager| that handles events by sending the converted events through a Dart hook to the framework.

This class interfaces with the HardwareKeyboard API in the framework.

A primary responder of |FlutterKeyboardManager| that handles events by sending the converted events through the embedder API.

This class communicates with the HardwareKeyboard API in the framework.

Definition at line 23 of file FlutterEmbedderKeyResponder.h.

Method Documentation

◆ adjustModifiers:

- (UInt32) adjustModifiers: (ios(13.4))  API_AVAILABLE
implementation

Because iOS differs from other platforms in that the modifier flags still contain the flag for the key that is being released on the keyup event, we adjust the modifiers when the released key is a matching modifier key.

Definition at line 405 of file FlutterEmbedderKeyResponder.mm.

811 :(nonnull FlutterUIPressProxy*)press API_AVAILABLE(ios(13.4)) {
812 if (@available(iOS 13.4, *)) {
813 // no-op
814 } else {
815 return press.key.modifierFlags;
816 }
817
818 bool keyDown = isKeyDown(press);
819
820 // Start with the current modifier flags, along with any sided flags that we
821 // already know are down.
822 UInt32 pressedModifiers =
823 press.key.modifierFlags | (_lastModifierFlagsOfInterest & kModifierFlagSidedMask);
824
825 pressedModifiers = [self fixSidedFlags:kModifierFlagShiftAny
826 withLeftFlag:kModifierFlagShiftLeft
827 withRightFlag:kModifierFlagShiftRight
828 withLeftKey:UIKeyboardHIDUsageKeyboardLeftShift
829 withRightKey:UIKeyboardHIDUsageKeyboardRightShift
830 withKeyCode:press.key.keyCode
831 keyDown:keyDown
832 forFlags:pressedModifiers];
833 pressedModifiers = [self fixSidedFlags:kModifierFlagControlAny
834 withLeftFlag:kModifierFlagControlLeft
835 withRightFlag:kModifierFlagControlRight
836 withLeftKey:UIKeyboardHIDUsageKeyboardLeftControl
837 withRightKey:UIKeyboardHIDUsageKeyboardRightControl
838 withKeyCode:press.key.keyCode
839 keyDown:keyDown
840 forFlags:pressedModifiers];
841 pressedModifiers = [self fixSidedFlags:kModifierFlagAltAny
842 withLeftFlag:kModifierFlagAltLeft
843 withRightFlag:kModifierFlagAltRight
844 withLeftKey:UIKeyboardHIDUsageKeyboardLeftAlt
845 withRightKey:UIKeyboardHIDUsageKeyboardRightAlt
846 withKeyCode:press.key.keyCode
847 keyDown:keyDown
848 forFlags:pressedModifiers];
849 pressedModifiers = [self fixSidedFlags:kModifierFlagMetaAny
850 withLeftFlag:kModifierFlagMetaLeft
851 withRightFlag:kModifierFlagMetaRight
852 withLeftKey:UIKeyboardHIDUsageKeyboardLeftGUI
853 withRightKey:UIKeyboardHIDUsageKeyboardRightGUI
854 withKeyCode:press.key.keyCode
855 keyDown:keyDown
856 forFlags:pressedModifiers];
857
858 if (press.key.keyCode == UIKeyboardHIDUsageKeyboardCapsLock) {
859 // The caps lock modifier needs to be unset only if it was already on
860 // and this is a key up. This is because it indicates the lock state, and
861 // not the key press state. The caps lock state should be on between the
862 // first down, and the second up (i.e. while the lock in effect), and
863 // this code turns it off at the second up event. The OS leaves it on still
864 // because of iOS's weird late processing of modifier states. Synthesis of
865 // the appropriate synthesized key events happens in synchronizeModifiers.
866 if (!keyDown && _lastModifierFlagsOfInterest & kModifierFlagCapsLock) {
867 pressedModifiers &= ~kModifierFlagCapsLock;
868 }
869 }
870 return pressedModifiers;
871}
UITextSmartQuotesType smartQuotesType API_AVAILABLE(ios(11.0))
@ kModifierFlagCapsLock
constexpr uint32_t kModifierFlagSidedMask
static bool isKeyDown(FlutterUIPressProxy *press) API_AVAILABLE(ios(13.4))
SK_API sk_sp< SkSurface > ios(9.0)

◆ fixSidedFlags:withLeftFlag:withRightFlag:withLeftKey:withRightKey:withKeyCode:keyDown:forFlags:

- (UInt32) fixSidedFlags: (ModifierFlag anyFlag
withLeftFlag: (ModifierFlag leftSide
withRightFlag: (ModifierFlag rightSide
withLeftKey: (UInt16)  leftKeyCode
withRightKey: (UInt16)  rightKeyCode
withKeyCode: (UInt16)  keyCode
keyDown: (BOOL isKeyDown
forFlags: (ios(13.4))  API_AVAILABLE 
implementation

Fix up the modifiers for a particular type of modifier key.

Definition at line 405 of file FlutterEmbedderKeyResponder.mm.

753 :(ModifierFlag)anyFlag
754 withLeftFlag:(ModifierFlag)leftSide
755 withRightFlag:(ModifierFlag)rightSide
756 withLeftKey:(UInt16)leftKeyCode
757 withRightKey:(UInt16)rightKeyCode
758 withKeyCode:(UInt16)keyCode
759 keyDown:(BOOL)isKeyDown
760 forFlags:(UInt32)modifiersPressed API_AVAILABLE(ios(13.4)) {
761 UInt32 newModifiers = modifiersPressed;
762 if (isKeyDown) {
763 // Add in the modifier flags that correspond to this key code, if any.
764 if (keyCode == leftKeyCode) {
765 newModifiers |= leftSide | anyFlag;
766 } else if (keyCode == rightKeyCode) {
767 newModifiers |= rightSide | anyFlag;
768 }
769 } else {
770 // If this is a key up, then remove any modifier that matches the keycode in
771 // the event from the flags, and the anyFlag if the other side isn't also
772 // pressed.
773 if (keyCode == leftKeyCode) {
774 newModifiers &= ~leftSide;
775 if (!(newModifiers & rightSide)) {
776 newModifiers &= ~anyFlag;
777 }
778 } else if (keyCode == rightKeyCode) {
779 newModifiers &= ~rightSide;
780 if (!(newModifiers & leftSide)) {
781 newModifiers &= ~anyFlag;
782 }
783 }
784 }
785
786 if (!(newModifiers & anyFlag)) {
787 // Turn off any sided flags, since the "any" flag is gone.
788 newModifiers &= ~(leftSide | rightSide);
789 }
790
791 return newModifiers;
792}
int BOOL

◆ getPressedState

- (nonnull NSDictionary *) getPressedState

Returns the keyboard pressed state.

Returns the keyboard pressed state. The dictionary contains one entry per pressed keys, mapping from the logical key to the physical key.

Definition at line 385 of file FlutterEmbedderKeyResponder.mm.

796 {
797 return [NSDictionary dictionaryWithDictionary:_pressingRecords];
798}

◆ handleCapsLockEvent:callback:

- (void) handleCapsLockEvent: (NSEvent*)  event
callback: (FlutterKeyCallbackGuard*)  callback 
implementation

Definition at line 385 of file FlutterEmbedderKeyResponder.mm.

726 [self synchronizeModifiers:event.modifierFlags
727 ignoringFlags:NSEventModifierFlagCapsLock
728 timestamp:event.timestamp
729 guard:callback];
730 if ((_lastModifierFlagsOfInterest & NSEventModifierFlagCapsLock) !=
731 (event.modifierFlags & NSEventModifierFlagCapsLock)) {
732 [self sendCapsLockTapWithTimestamp:event.timestamp synthesizeDown:false callback:callback];
733 _lastModifierFlagsOfInterest = _lastModifierFlagsOfInterest ^ NSEventModifierFlagCapsLock;
734 } else {
735 [callback resolveTo:TRUE];
736 }
737}
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
FlKeyEvent * event

◆ handleDownEvent:callback:

- (void) handleDownEvent: (NSEvent*)  event
callback: (FlutterKeyCallbackGuard*)  callback 
implementation

Definition at line 385 of file FlutterEmbedderKeyResponder.mm.

646 uint64_t physicalKey = GetPhysicalKeyForKeyCode(event.keyCode);
647 NSNumber* logicalKeyFromMap = self.layoutMap[@(event.keyCode)];
648 uint64_t logicalKey = logicalKeyFromMap != nil ? [logicalKeyFromMap unsignedLongLongValue]
649 : GetLogicalKeyForEvent(event, physicalKey);
650 [self synchronizeModifiers:event.modifierFlags
651 ignoringFlags:0
652 timestamp:event.timestamp
653 guard:callback];
654
655 bool isARepeat = event.isARepeat;
656 NSNumber* pressedLogicalKey = _pressingRecords[@(physicalKey)];
657 if (pressedLogicalKey != nil && !isARepeat) {
658 // This might happen in add-to-app scenarios if the focus is changed
659 // from the native view to the Flutter view amid the key tap.
660 //
661 // This might also happen when a key event is forged (such as by an
662 // IME) using the same keyCode as an unreleased key. See
663 // https://github.com/flutter/flutter/issues/82673#issuecomment-988661079
664 FlutterKeyEvent flutterEvent = {
665 .struct_size = sizeof(FlutterKeyEvent),
666 .timestamp = GetFlutterTimestampFrom(event.timestamp),
668 .physical = physicalKey,
669 .logical = [pressedLogicalKey unsignedLongLongValue],
670 .character = nil,
671 .synthesized = true,
672 };
673 [self sendSynthesizedFlutterEvent:flutterEvent guard:callback];
674 pressedLogicalKey = nil;
675 }
676
677 if (pressedLogicalKey == nil) {
678 [self updateKey:physicalKey asPressed:logicalKey];
679 }
680
681 FlutterKeyEvent flutterEvent = {
682 .struct_size = sizeof(FlutterKeyEvent),
683 .timestamp = GetFlutterTimestampFrom(event.timestamp),
684 .type = pressedLogicalKey == nil ? kFlutterKeyEventTypeDown : kFlutterKeyEventTypeRepeat,
685 .physical = physicalKey,
686 .logical = pressedLogicalKey == nil ? logicalKey : [pressedLogicalKey unsignedLongLongValue],
687 .character = getEventString(event.characters),
688 .synthesized = false,
689 };
690 [self sendPrimaryFlutterEvent:flutterEvent callback:callback];
691}
@ kFlutterKeyEventTypeDown
Definition embedder.h:1074
@ kFlutterKeyEventTypeUp
Definition embedder.h:1073
@ kFlutterKeyEventTypeRepeat
Definition embedder.h:1075
size_t struct_size
The size of this struct. Must be sizeof(FlutterKeyEvent).
Definition embedder.h:1110

◆ handleEvent:callback:

- (void) handleEvent: (NSEvent*)  event
callback: (FlutterAsyncKeyCallback callback 
implementation

Definition at line 385 of file FlutterEmbedderKeyResponder.mm.

491 // The conversion algorithm relies on a non-nil callback to properly compute
492 // `synthesized`.
493 NSAssert(callback != nil, @"The callback must not be nil.");
494 FlutterKeyCallbackGuard* guardedCallback =
495 [[FlutterKeyCallbackGuard alloc] initWithCallback:callback];
496 switch (event.type) {
497 case NSEventTypeKeyDown:
498 [self handleDownEvent:event callback:guardedCallback];
499 break;
500 case NSEventTypeKeyUp:
501 [self handleUpEvent:event callback:guardedCallback];
502 break;
503 case NSEventTypeFlagsChanged:
504 [self handleFlagEvent:event callback:guardedCallback];
505 break;
506 default:
507 NSAssert(false, @"Unexpected key event type: |%@|.", @(event.type));
508 }
509 NSAssert(guardedCallback.handled, @"The callback is returned without being handled.");
510 if (!guardedCallback.sentAnyEvents) {
511 FlutterKeyEvent flutterEvent = {
512 .struct_size = sizeof(FlutterKeyEvent),
513 .timestamp = 0,
515 .physical = 0,
516 .logical = 0,
517 .character = nil,
518 .synthesized = false,
519 };
520 _sendEvent(flutterEvent, nullptr, nullptr);
521 }
522 NSAssert(_lastModifierFlagsOfInterest == (event.modifierFlags & _modifierFlagOfInterestMask),
523 @"The modifier flags are not properly updated: recorded 0x%lx, event with mask 0x%lx",
524 _lastModifierFlagsOfInterest, event.modifierFlags & _modifierFlagOfInterestMask);
525}
void(^ FlutterAsyncKeyCallback)(BOOL handled)

◆ handleFlagEvent:callback:

- (void) handleFlagEvent: (NSEvent*)  event
callback: (FlutterKeyCallbackGuard*)  callback 
implementation

Definition at line 385 of file FlutterEmbedderKeyResponder.mm.

740 NSNumber* targetModifierFlagObj = flutter::keyCodeToModifierFlag[@(event.keyCode)];
741 NSUInteger targetModifierFlag =
742 targetModifierFlagObj == nil ? 0 : [targetModifierFlagObj unsignedLongValue];
743 uint64_t targetKey = GetPhysicalKeyForKeyCode(event.keyCode);
744 if (targetKey == flutter::kCapsLockPhysicalKey) {
745 return [self handleCapsLockEvent:event callback:callback];
746 }
747
748 [self synchronizeModifiers:event.modifierFlags
749 ignoringFlags:targetModifierFlag
750 timestamp:event.timestamp
751 guard:callback];
752
753 NSNumber* pressedLogicalKey = [_pressingRecords objectForKey:@(targetKey)];
754 BOOL lastTargetPressed = pressedLogicalKey != nil;
755 NSAssert(targetModifierFlagObj == nil ||
756 (_lastModifierFlagsOfInterest & targetModifierFlag) != 0 == lastTargetPressed,
757 @"Desynchronized state between lastModifierFlagsOfInterest (0x%lx) on bit 0x%lx "
758 @"for keyCode 0x%hx, whose pressing state is %@.",
759 _lastModifierFlagsOfInterest, targetModifierFlag, event.keyCode,
760 lastTargetPressed
761 ? [NSString stringWithFormat:@"0x%llx", [pressedLogicalKey unsignedLongLongValue]]
762 : @"empty");
763
764 BOOL shouldBePressed = (event.modifierFlags & targetModifierFlag) != 0;
765 if (lastTargetPressed == shouldBePressed) {
766 [callback resolveTo:TRUE];
767 return;
768 }
769 _lastModifierFlagsOfInterest = _lastModifierFlagsOfInterest ^ targetModifierFlag;
770 [self sendModifierEventOfType:shouldBePressed
771 timestamp:event.timestamp
772 keyCode:event.keyCode
773 synthesized:false
774 callback:callback];
775}
const std::map< uint32_t, ModifierFlag > keyCodeToModifierFlag
const uint64_t kCapsLockPhysicalKey

◆ handlePress:callback:

- (void) handlePress: (nonnull FlutterUIPressProxy *)  press
callback: (ios(13.4))  API_AVAILABLE 
implementation

Process the event.

The |callback| should be called with a value that indicates whether the responder has handled the given press event. The |callback| must be called exactly once, and can be called before the return of this method, or after.

Reimplemented from <FlutterKeyPrimaryResponder>.

Definition at line 405 of file FlutterEmbedderKeyResponder.mm.

503 :(nonnull FlutterUIPressProxy*)press
505 if (@available(iOS 13.4, *)) {
506 } else {
507 return;
508 }
509 // The conversion algorithm relies on a non-nil callback to properly compute
510 // `synthesized`.
511 NSAssert(callback != nil, @"The callback must not be nil.");
512
513 FlutterKeyCallbackGuard* guardedCallback = nil;
514 switch (press.phase) {
515 case UIPressPhaseBegan:
516 guardedCallback = [[FlutterKeyCallbackGuard alloc] initWithCallback:callback];
517 [self handlePressBegin:press callback:guardedCallback];
518 break;
519 case UIPressPhaseEnded:
520 guardedCallback = [[FlutterKeyCallbackGuard alloc] initWithCallback:callback];
521 [self handlePressEnd:press callback:guardedCallback];
522 break;
523 case UIPressPhaseChanged:
524 case UIPressPhaseCancelled:
525 // TODO(gspencergoog): Handle cancelled events as synthesized up events.
526 case UIPressPhaseStationary:
527 NSAssert(false, @"Unexpected press phase receieved in handlePress");
528 return;
529 }
530 NSAssert(guardedCallback.handled, @"The callback returned without being handled.");
531 NSAssert(
532 (_lastModifierFlagsOfInterest & ~kModifierFlagCapsLock) ==
533 ([self adjustModifiers:press] & (_modifierFlagOfInterestMask & ~kModifierFlagCapsLock)),
534 @"The modifier flags are not properly updated: recorded 0x%lx, event with mask 0x%lx",
535 static_cast<unsigned long>(_lastModifierFlagsOfInterest & ~kModifierFlagCapsLock),
536 static_cast<unsigned long>([self adjustModifiers:press] &
537 (_modifierFlagOfInterestMask & ~kModifierFlagCapsLock)));
538}

◆ handlePressBegin:callback:

- (void) handlePressBegin: (nonnull FlutterUIPressProxy *)  press
callback: (ios(13.4))  API_AVAILABLE 
implementation

Processes a down event from the system.

Definition at line 405 of file FlutterEmbedderKeyResponder.mm.

667 :(nonnull FlutterUIPressProxy*)press
669 if (@available(iOS 13.4, *)) {
670 } else {
671 return;
672 }
673 uint64_t physicalKey = GetPhysicalKeyForKeyCode(press.key.keyCode);
674 // Some unprintable keys on iOS have literal names on their key label, such as
675 // @"UIKeyInputEscape". They are called the "special keys" and have predefined
676 // logical keys and empty characters.
677 NSNumber* specialKey = [specialKeyMapping objectForKey:press.key.charactersIgnoringModifiers];
678 uint64_t logicalKey = GetLogicalKeyForEvent(press, specialKey);
679 [self synchronizeModifiers:press];
680
681 NSNumber* pressedLogicalKey = nil;
682 if ([_pressingRecords count] > 0) {
683 pressedLogicalKey = _pressingRecords[@(physicalKey)];
684 if (pressedLogicalKey != nil) {
685 // Normally the key up events won't be missed since iOS always sends the
686 // key up event to the view where the corresponding key down occurred.
687 // However this might happen in add-to-app scenarios if the focus is changed
688 // from the native view to the Flutter view amid the key tap.
689 [callback resolveTo:TRUE];
690 [self sendEmptyEvent];
691 return;
692 }
693 }
694
695 if (pressedLogicalKey == nil) {
696 [self updateKey:physicalKey asPressed:logicalKey];
697 }
698
699 FlutterKeyEvent flutterEvent = {
700 .struct_size = sizeof(FlutterKeyEvent),
701 .timestamp = GetFlutterTimestampFrom(press.timestamp),
703 .physical = physicalKey,
704 .logical = pressedLogicalKey == nil ? logicalKey : [pressedLogicalKey unsignedLongLongValue],
705 .character =
706 specialKey != nil ? nil : getEventCharacters(press.key.characters, press.key.keyCode),
707 .synthesized = false,
709 };
710 [self sendPrimaryFlutterEvent:flutterEvent callback:callback];
711}
int count
@ kFlutterKeyEventDeviceTypeKeyboard
Definition embedder.h:1079
static double GetFlutterTimestampFrom(NSTimeInterval timestamp)
static const char * getEventCharacters(NSString *characters, UIKeyboardHIDUsage keyCode) API_AVAILABLE(ios(13.4))
static uint64_t GetPhysicalKeyForKeyCode(UInt32 keyCode)
static uint64_t GetLogicalKeyForEvent(FlutterUIPressProxy *press, NSNumber *maybeSpecialKey) API_AVAILABLE(ios(13.4))

◆ handlePressEnd:callback:

- (void) handlePressEnd: (nonnull FlutterUIPressProxy *)  press
callback: (ios(13.4))  API_AVAILABLE 
implementation

Processes an up event from the system.

Definition at line 405 of file FlutterEmbedderKeyResponder.mm.

713 :(nonnull FlutterUIPressProxy*)press
715 if (@available(iOS 13.4, *)) {
716 } else {
717 return;
718 }
719 [self synchronizeModifiers:press];
720
721 uint64_t physicalKey = GetPhysicalKeyForKeyCode(press.key.keyCode);
722 NSNumber* pressedLogicalKey = _pressingRecords[@(physicalKey)];
723 if (pressedLogicalKey == nil) {
724 // Normally the key up events won't be missed since iOS always sends the
725 // key up event to the view where the corresponding key down occurred.
726 // However this might happen in add-to-app scenarios if the focus is changed
727 // from the native view to the Flutter view amid the key tap.
728 [callback resolveTo:TRUE];
729 [self sendEmptyEvent];
730 return;
731 }
732 [self updateKey:physicalKey asPressed:0];
733
734 FlutterKeyEvent flutterEvent = {
735 .struct_size = sizeof(FlutterKeyEvent),
736 .timestamp = GetFlutterTimestampFrom(press.timestamp),
738 .physical = physicalKey,
739 .logical = [pressedLogicalKey unsignedLongLongValue],
740 .character = nil,
741 .synthesized = false,
743 };
744 [self sendPrimaryFlutterEvent:flutterEvent callback:callback];
745}

◆ handleResponse:forId:

- (void) handleResponse: (BOOL handled
forId: (uint64_t)  responseId 
implementation

Processes the response from the framework.

Definition at line 405 of file FlutterEmbedderKeyResponder.mm.

747 :(BOOL)handled forId:(uint64_t)responseId {
748 FlutterAsyncKeyCallback callback = _pendingResponses[@(responseId)];
749 callback(handled);
750 [_pendingResponses removeObjectForKey:@(responseId)];
751}

◆ handleUpEvent:callback:

- (void) handleUpEvent: (NSEvent*)  event
callback: (FlutterKeyCallbackGuard*)  callback 
implementation

Definition at line 385 of file FlutterEmbedderKeyResponder.mm.

694 NSAssert(!event.isARepeat, @"Unexpected repeated Up event: keyCode %d, char %@, charIM %@",
695 event.keyCode, event.characters, event.charactersIgnoringModifiers);
696 [self synchronizeModifiers:event.modifierFlags
697 ignoringFlags:0
698 timestamp:event.timestamp
699 guard:callback];
700
701 uint64_t physicalKey = GetPhysicalKeyForKeyCode(event.keyCode);
702 NSNumber* pressedLogicalKey = _pressingRecords[@(physicalKey)];
703 if (pressedLogicalKey == nil) {
704 // Normally the key up events won't be missed since macOS always sends the
705 // key up event to the window where the corresponding key down occurred.
706 // However this might happen in add-to-app scenarios if the focus is changed
707 // from the native view to the Flutter view amid the key tap.
708 [callback resolveTo:TRUE];
709 return;
710 }
711 [self updateKey:physicalKey asPressed:0];
712
713 FlutterKeyEvent flutterEvent = {
714 .struct_size = sizeof(FlutterKeyEvent),
715 .timestamp = GetFlutterTimestampFrom(event.timestamp),
717 .physical = physicalKey,
718 .logical = [pressedLogicalKey unsignedLongLongValue],
719 .character = nil,
720 .synthesized = false,
721 };
722 [self sendPrimaryFlutterEvent:flutterEvent callback:callback];
723}

◆ initWithSendEvent: [1/3]

- (nonnull instancetype) initWithSendEvent: (_Nonnull FlutterSendEmbedderKeyEvent sendEvent

Create an instance by specifying the function to send converted events to.

The |sendEvent| is typically |FlutterEngine|'s |sendKeyEvent|.

◆ initWithSendEvent: [2/3]

- (nonnull instancetype) initWithSendEvent: (FlutterSendKeyEvent sendEvent
implementation

Definition at line 405 of file FlutterEmbedderKeyResponder.mm.

490 :(FlutterSendKeyEvent)sendEvent {
491 self = [super init];
492 if (self != nil) {
493 _sendEvent = [sendEvent copy];
494 _pressingRecords = [[NSMutableDictionary alloc] init];
495 _pendingResponses = [[NSMutableDictionary alloc] init];
496 _responseId = 1;
497 _lastModifierFlagsOfInterest = 0;
498 _modifierFlagOfInterestMask = computeModifierFlagOfInterestMask();
499 }
500 return self;
501}
void(^ FlutterSendKeyEvent)(const FlutterKeyEvent &, _Nullable FlutterKeyEventCallback, void *_Nullable)
static NSUInteger computeModifierFlagOfInterestMask()

◆ initWithSendEvent: [3/3]

- (nonnull instancetype) initWithSendEvent: (nonnull FlutterSendKeyEvent sendEvent

Create an instance by specifying the function to send converted events to.

The |sendEvent| is typically |FlutterEngine|'s |sendKeyEvent|.

◆ sendCapsLockTapWithTimestamp:synthesizeDown:callback:

- (void) sendCapsLockTapWithTimestamp: (NSTimeInterval)  timestamp
synthesizeDown: (bool)  synthesizeDown
callback: (FlutterKeyCallbackGuard*)  callback 
implementation

Definition at line 385 of file FlutterEmbedderKeyResponder.mm.

588 :(NSTimeInterval)timestamp
589 synthesizeDown:(bool)synthesizeDown
591 // MacOS sends a down *or* an up when CapsLock is tapped, alternatively on
592 // even taps and odd taps. A CapsLock down or CapsLock up should always be
593 // converted to a down *and* an up, and the up should always be a synthesized
594 // event, since the FlutterEmbedderKeyResponder will never know when the
595 // button is released.
596 FlutterKeyEvent flutterEvent = {
597 .struct_size = sizeof(FlutterKeyEvent),
598 .timestamp = GetFlutterTimestampFrom(timestamp),
602 .character = nil,
603 .synthesized = synthesizeDown,
604 };
605 if (!synthesizeDown) {
606 [self sendPrimaryFlutterEvent:flutterEvent callback:callback];
607 } else {
608 [self sendSynthesizedFlutterEvent:flutterEvent guard:callback];
609 }
610
611 flutterEvent.type = kFlutterKeyEventTypeUp;
612 flutterEvent.synthesized = true;
613 [self sendSynthesizedFlutterEvent:flutterEvent guard:callback];
614}
const uint64_t kCapsLockLogicalKey

◆ sendEmptyEvent

- (void) sendEmptyEvent
implementation

Send an empty key event.

The event is never synthesized, and never expects an event result. An empty event is sent when no other events should be sent, such as upon back-to-back keydown events of the same key.

Definition at line 405 of file FlutterEmbedderKeyResponder.mm.

631 {
632 FlutterKeyEvent event = {
633 .struct_size = sizeof(FlutterKeyEvent),
634 .timestamp = 0,
636 .physical = 0,
637 .logical = 0,
638 .character = nil,
639 .synthesized = false,
641 };
642 _sendEvent(event, nil, nil);
643}

◆ sendModifierEventOfType:timestamp:keyCode:synthesized:callback:

- (void) sendModifierEventOfType: (BOOL isDownEvent
timestamp: (NSTimeInterval)  timestamp
keyCode: (unsigned short)  keyCode
synthesized: (bool)  synthesized
callback: (FlutterKeyCallbackGuard*)  callback 
implementation

Definition at line 385 of file FlutterEmbedderKeyResponder.mm.

616 :(BOOL)isDownEvent
617 timestamp:(NSTimeInterval)timestamp
618 keyCode:(unsigned short)keyCode
619 synthesized:(bool)synthesized
621 uint64_t physicalKey = GetPhysicalKeyForKeyCode(keyCode);
622 uint64_t logicalKey = GetLogicalKeyForModifier(keyCode, physicalKey);
623 if (physicalKey == 0 || logicalKey == 0) {
624 NSLog(@"Unrecognized modifier key: keyCode 0x%hx, physical key 0x%llx", keyCode, physicalKey);
625 [callback resolveTo:TRUE];
626 return;
627 }
628 FlutterKeyEvent flutterEvent = {
629 .struct_size = sizeof(FlutterKeyEvent),
630 .timestamp = GetFlutterTimestampFrom(timestamp),
631 .type = isDownEvent ? kFlutterKeyEventTypeDown : kFlutterKeyEventTypeUp,
632 .physical = physicalKey,
633 .logical = logicalKey,
634 .character = nil,
635 .synthesized = synthesized,
636 };
637 [self updateKey:physicalKey asPressed:isDownEvent ? logicalKey : 0];
638 if (!synthesized) {
639 [self sendPrimaryFlutterEvent:flutterEvent callback:callback];
640 } else {
641 [self sendSynthesizedFlutterEvent:flutterEvent guard:callback];
642 }
643}

◆ sendPrimaryFlutterEvent:callback:

- (void) sendPrimaryFlutterEvent: (const FlutterKeyEvent&)  event
callback: (FlutterKeyCallbackGuard*)  callback 
implementation

Definition at line 405 of file FlutterEmbedderKeyResponder.mm.

621 :(const FlutterKeyEvent&)event
623 _responseId += 1;
624 uint64_t responseId = _responseId;
626 [[FlutterKeyPendingResponse alloc] initWithHandler:self responseId:responseId];
627 [callback pendTo:_pendingResponses withId:responseId];
628 _sendEvent(event, HandleResponse, (__bridge_retained void* _Nullable)pending);
629}
void pendTo:withId:(nonnull NSMutableDictionary< NSNumber *, FlutterAsyncKeyCallback > *pendingResponses,[withId] uint64_t responseId)

◆ sendSynthesizedFlutterEvent:guard:

- (void) sendSynthesizedFlutterEvent: (const FlutterKeyEvent &)  event
guard: (FlutterKeyCallbackGuard *)  guard 
implementation

Send a synthesized key event, never expecting its event result.

The |guard| is basically a regular guarded callback, but instead of being called, it is only used to record whether an event is sent.

Definition at line 385 of file FlutterEmbedderKeyResponder.mm.

582 :(const FlutterKeyEvent&)event
583 guard:(FlutterKeyCallbackGuard*)guard {
584 _sendEvent(event, nullptr, nullptr);
585 guard.sentAnyEvents = TRUE;
586}

◆ synchronizeModifiers:

- (void) synchronizeModifiers: (ios(13.4))  API_AVAILABLE
implementation

Compare the last modifier flags and the current, and dispatch synthesized key events for each different modifier flag bit.

The flags compared are all flags after masking with |modifierFlagOfInterestMask| and excluding |ignoringFlags|.

Definition at line 405 of file FlutterEmbedderKeyResponder.mm.

542 :(nonnull FlutterUIPressProxy*)press API_AVAILABLE(ios(13.4)) {
543 if (@available(iOS 13.4, *)) {
544 } else {
545 return;
546 }
547
548 const UInt32 lastFlagsOfInterest = _lastModifierFlagsOfInterest & _modifierFlagOfInterestMask;
549 const UInt32 pressedModifiers = [self adjustModifiers:press];
550 const UInt32 currentFlagsOfInterest = pressedModifiers & _modifierFlagOfInterestMask;
551 UInt32 flagDifference = currentFlagsOfInterest ^ lastFlagsOfInterest;
552 if (flagDifference & kModifierFlagCapsLock) {
553 // If the caps lock changed, and we didn't expect that, then send a
554 // synthesized down and an up to simulate a toggle of the state.
555 if (press.key.keyCode != UIKeyboardHIDUsageKeyboardCapsLock) {
556 [self synthesizeCapsLockTapWithTimestamp:press.timestamp];
557 }
558 flagDifference &= ~kModifierFlagCapsLock;
559 }
560 while (true) {
561 const UInt32 currentFlag = lowestSetBit(flagDifference);
562 if (currentFlag == 0) {
563 break;
564 }
565 flagDifference &= ~currentFlag;
566 if (currentFlag & kModifierFlagAnyMask) {
567 // Skip synthesizing keys for the "any" flags, since their synthesis will
568 // be handled when we do the sided flags. We still want them in the flags
569 // of interest, though, so we can keep their state.
570 continue;
571 }
572 auto keyCode = modifierFlagToKeyCode.find(static_cast<ModifierFlag>(currentFlag));
573 NSAssert(keyCode != modifierFlagToKeyCode.end(), @"Invalid modifier flag of interest 0x%lx",
574 static_cast<unsigned long>(currentFlag));
575 if (keyCode == modifierFlagToKeyCode.end()) {
576 continue;
577 }
578 // If this press matches the modifier key in question, then don't synthesize
579 // it, because it's already a "real" keypress.
580 if (keyCode->second == static_cast<UInt32>(press.key.keyCode)) {
581 continue;
582 }
583 BOOL isDownEvent = currentFlagsOfInterest & currentFlag;
584 [self synthesizeModifierEventOfType:isDownEvent
585 timestamp:press.timestamp
586 keyCode:keyCode->second];
587 }
588 _lastModifierFlagsOfInterest =
589 (_lastModifierFlagsOfInterest & ~_modifierFlagOfInterestMask) | currentFlagsOfInterest;
590}
const std::map< ModifierFlag, uint32_t > modifierFlagToKeyCode
constexpr uint32_t kModifierFlagAnyMask
static NSUInteger lowestSetBit(NSUInteger bitmask)

◆ synchronizeModifiers:ignoringFlags:timestamp:guard:

- (void) synchronizeModifiers: (NSUInteger)  currentFlags
ignoringFlags: (NSUInteger)  ignoringFlags
timestamp: (NSTimeInterval)  timestamp
guard: (FlutterKeyCallbackGuard*)  guard 
implementation

Definition at line 385 of file FlutterEmbedderKeyResponder.mm.

529 :(NSUInteger)currentFlags
530 ignoringFlags:(NSUInteger)ignoringFlags
531 timestamp:(NSTimeInterval)timestamp
532 guard:(FlutterKeyCallbackGuard*)guard {
533 const NSUInteger updatingMask = _modifierFlagOfInterestMask & ~ignoringFlags;
534 const NSUInteger currentFlagsOfInterest = currentFlags & updatingMask;
535 const NSUInteger lastFlagsOfInterest = _lastModifierFlagsOfInterest & updatingMask;
536 NSUInteger flagDifference = currentFlagsOfInterest ^ lastFlagsOfInterest;
537 if (flagDifference & NSEventModifierFlagCapsLock) {
538 [self sendCapsLockTapWithTimestamp:timestamp synthesizeDown:true callback:guard];
539 flagDifference = flagDifference & ~NSEventModifierFlagCapsLock;
540 }
541 while (true) {
542 const NSUInteger currentFlag = lowestSetBit(flagDifference);
543 if (currentFlag == 0) {
544 break;
545 }
546 flagDifference = flagDifference & ~currentFlag;
547 NSNumber* keyCode = [flutter::modifierFlagToKeyCode objectForKey:@(currentFlag)];
548 NSAssert(keyCode != nil, @"Invalid modifier flag 0x%lx", currentFlag);
549 if (keyCode == nil) {
550 continue;
551 }
552 BOOL isDownEvent = (currentFlagsOfInterest & currentFlag) != 0;
553 [self sendModifierEventOfType:isDownEvent
554 timestamp:timestamp
555 keyCode:[keyCode unsignedShortValue]
556 synthesized:true
557 callback:guard];
558 }
559 _lastModifierFlagsOfInterest =
560 (_lastModifierFlagsOfInterest & ~updatingMask) | currentFlagsOfInterest;
561}

◆ syncModifiersIfNeeded:timestamp:

- (void) syncModifiersIfNeeded: (NSEventModifierFlags)  modifierFlags
timestamp: (NSTimeInterval)  timestamp 

Synthesize modifier keys events.

If needed, synthesize modifier keys up and down events by comparing their current pressing states with the given modifier flags.

Reimplemented from <FlutterKeyPrimaryResponder>.

Definition at line 385 of file FlutterEmbedderKeyResponder.mm.

783 :(NSEventModifierFlags)modifierFlags
784 timestamp:(NSTimeInterval)timestamp {
785 FlutterAsyncKeyCallback replyCallback = ^(BOOL handled) {
786 // Do nothing.
787 };
788 FlutterKeyCallbackGuard* guardedCallback =
789 [[FlutterKeyCallbackGuard alloc] initWithCallback:replyCallback];
790 [self synchronizeModifiers:modifierFlags
791 ignoringFlags:0
792 timestamp:timestamp
793 guard:guardedCallback];
794}

◆ synthesizeCapsLockTapWithTimestamp:

- (void) synthesizeCapsLockTapWithTimestamp: (NSTimeInterval)  timestamp
implementation

Synthesize a CapsLock down event, then a CapsLock up event.

Definition at line 405 of file FlutterEmbedderKeyResponder.mm.

592 :(NSTimeInterval)timestamp {
593 // The assumption when the app starts is that caps lock is off, but if that
594 // turns out to be untrue (according to the modifier flags), then this is used
595 // to simulate a key down and a key up of the caps lock key, to simulate
596 // toggling of that state in the framework.
597 FlutterKeyEvent flutterEvent = {
598 .struct_size = sizeof(FlutterKeyEvent),
599 .timestamp = GetFlutterTimestampFrom(timestamp),
601 .physical = kCapsLockPhysicalKey,
602 .logical = kCapsLockLogicalKey,
603 .character = nil,
604 .synthesized = true,
606 };
607 _sendEvent(flutterEvent, nullptr, nullptr);
608
609 flutterEvent.type = kFlutterKeyEventTypeUp;
610 _sendEvent(flutterEvent, nullptr, nullptr);
611}
const uint64_t kCapsLockLogicalKey
const uint64_t kCapsLockPhysicalKey
FlutterKeyEventType type
The event kind.
Definition embedder.h:1116

◆ synthesizeModifierEventOfType:timestamp:keyCode:

- (void) synthesizeModifierEventOfType: (BOOL isDownEvent
timestamp: (NSTimeInterval)  timestamp
keyCode: (UInt32)  keyCode 
implementation

Send a key event for a modifier key.

Definition at line 405 of file FlutterEmbedderKeyResponder.mm.

645 :(BOOL)isDownEvent
646 timestamp:(NSTimeInterval)timestamp
647 keyCode:(UInt32)keyCode {
648 uint64_t physicalKey = GetPhysicalKeyForKeyCode(keyCode);
649 uint64_t logicalKey = GetLogicalKeyForModifier(keyCode, physicalKey);
650 if (physicalKey == 0 || logicalKey == 0) {
651 return;
652 }
653 FlutterKeyEvent flutterEvent = {
654 .struct_size = sizeof(FlutterKeyEvent),
655 .timestamp = GetFlutterTimestampFrom(timestamp),
656 .type = isDownEvent ? kFlutterKeyEventTypeDown : kFlutterKeyEventTypeUp,
657 .physical = physicalKey,
658 .logical = logicalKey,
659 .character = nil,
660 .synthesized = true,
662 };
663 [self updateKey:physicalKey asPressed:isDownEvent ? logicalKey : 0];
664 _sendEvent(flutterEvent, nullptr, nullptr);
665}
static uint64_t GetLogicalKeyForModifier(UInt32 keyCode, uint64_t hidCode)

◆ updateKey:asPressed:

- (void) updateKey: (uint64_t)  physicalKey
asPressed: (uint64_t)  logicalKey 
implementation

Update the pressing state.

If logicalKey is not 0, physicalKey is pressed as logicalKey. Otherwise, physicalKey is released.

Definition at line 405 of file FlutterEmbedderKeyResponder.mm.

613 :(uint64_t)physicalKey asPressed:(uint64_t)logicalKey {
614 if (logicalKey == 0) {
615 [_pressingRecords removeObjectForKey:@(physicalKey)];
616 } else {
617 _pressingRecords[@(physicalKey)] = @(logicalKey);
618 }
619}
if(end==-1)

Property Documentation

◆ lastModifierFlagsOfInterest

- (NSUInteger) lastModifierFlagsOfInterest
readwritenonatomicassignimplementation

The modifier flags of the last received key event, excluding uninterested bits.

This should be kept synchronized with the last |NSEvent.modifierFlags| after masking with |modifierFlagOfInterestMask|. This should also be kept synchronized with the corresponding keys of |pressingRecords|.

This is used by |synchronizeModifiers| to quickly find out modifier keys that are desynchronized.

Provided by category FlutterEmbedderKeyResponder().

Definition at line 391 of file FlutterEmbedderKeyResponder.mm.

◆ modifierFlagOfInterestMask

- (NSUInteger) modifierFlagOfInterestMask
readwritenonatomicassignimplementation

A constant mask for NSEvent.modifierFlags that Flutter synchronizes with.

Flutter keeps track of the last |modifierFlags| and compares it with the incoming one. Any bit within |modifierFlagOfInterestMask| that is different (except for the one that corresponds to the event key) indicates that an event for this modifier was missed, and Flutter synthesizes an event to make up for the state difference.

It is computed by computeModifierFlagOfInterestMask.

Provided by category FlutterEmbedderKeyResponder().

Definition at line 378 of file FlutterEmbedderKeyResponder.mm.

◆ pendingResponses

- (NSMutableDictionary<NSNumber*, FlutterAsyncKeyCallback>*) pendingResponses
readwritenonatomicassignimplementation

A map of unresponded key events sent to the framework.

Its values are |responseId|s, and keys are the callback that was received along with the event.

Provided by category FlutterEmbedderKeyResponder().

Definition at line 405 of file FlutterEmbedderKeyResponder.mm.

◆ pressingRecords

- (NSMutableDictionary<NSNumber*, NSNumber*>*) pressingRecords
readwritenonatomicassignimplementation

A map of pressed keys.

The keys of the dictionary are physical keys, while the values are the logical keys of the key down event.

A map of presessd keys.

The keys of the dictionary are physical keys, while the values are the logical keys of the key down event.

Provided by category FlutterEmbedderKeyResponder().

Definition at line 365 of file FlutterEmbedderKeyResponder.mm.

◆ responseId

- (uint64_t) responseId
readwritenonatomicassignimplementation

A self-incrementing ID used to label key events sent to the framework.

Provided by category FlutterEmbedderKeyResponder().

Definition at line 396 of file FlutterEmbedderKeyResponder.mm.

◆ sendEvent [1/2]

- (FlutterSendKeyEvent) sendEvent
readnonatomiccopyimplementation

The function to send converted events to.

Set by the initializer.

Provided by category FlutterEmbedderKeyResponder(Tests).

Definition at line 357 of file FlutterEmbedderKeyResponder.mm.

◆ sendEvent [2/2]

- (FlutterSendEmbedderKeyEvent) sendEvent
readwritenonatomiccopyimplementation

The function to send converted events to.

Set by the initializer.

Provided by category FlutterEmbedderKeyResponder().

Definition at line 338 of file FlutterEmbedderKeyResponder.mm.


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