5#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEmbedderKeyResponder.h"
6#include <objc/NSObjCRuntime.h>
8#import <objc/message.h>
13#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h"
14#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h"
31 return bitmask & -bitmask;
41 unichar codeUnit = [label characterAtIndex:0];
42 return (codeUnit <= 0x1f && codeUnit >= 0x00) || (codeUnit >= 0x7f && codeUnit <= 0x9f);
52 unichar codeUnit = [label characterAtIndex:0];
53 return codeUnit >= 0xF700 && codeUnit <= 0xF8FF;
66static uint64_t
KeyOfPlane(uint64_t baseKey, uint64_t plane) {
78 return physicalKey->second;
87 return fromKeyCode->second;
99 constexpr uint64_t lower_a = 0x61;
100 constexpr uint64_t upper_a = 0x41;
101 constexpr uint64_t upper_z = 0x5a;
103 constexpr uint64_t lower_a_grave = 0xe0;
104 constexpr uint64_t upper_a_grave = 0xc0;
105 constexpr uint64_t upper_thorn = 0xde;
106 constexpr uint64_t division = 0xf7;
109 if (n >= upper_a && n <= upper_z) {
110 return n - upper_a + lower_a;
114 if (n >= upper_a_grave && n <= upper_thorn && n != division) {
115 return n - upper_a_grave + lower_a_grave;
126 if (characters == nil) {
129 if ([characters
length] == 0) {
132 if (@available(iOS 13.4, *)) {
146 return [characters UTF8String];
160 if (maybeSpecialKey != nil) {
161 return [maybeSpecialKey unsignedLongLongValue];
166 return fromKeyCode->second;
168 const char* characters =
171 characters ==
nullptr ? nil : [[NSString alloc] initWithUTF8String:characters];
172 NSUInteger keyLabelLength = [keyLabel
length];
183 NSCAssert((keyLabelLength < 2),
@"Unexpected long key label: |%@|.", keyLabel);
185 uint64_t codeUnit = (uint64_t)[keyLabel characterAtIndex:0];
186 if (keyLabelLength == 2) {
187 uint64_t secondCode = (uint64_t)[keyLabel characterAtIndex:1];
188 codeUnit = (codeUnit << 16) | secondCode;
203 return timestamp * 1000000.0;
216 modifierFlagOfInterestMask = modifierFlagOfInterestMask | entry.second;
218 return modifierFlagOfInterestMask;
222 switch (press.phase) {
223 case UIPressPhaseStationary:
224 case UIPressPhaseChanged:
228 case UIPressPhaseBegan:
230 case UIPressPhaseCancelled:
231 case UIPressPhaseEnded:
263 responseId:(uint64_t)responseId;
269 responseId:(uint64_t)responseId {
294 withId:(uint64_t)responseId;
299- (void)resolveTo:(
BOOL)handled;
319 _callback = [callback copy];
326 withId:(uint64_t)responseId {
327 NSAssert(!_handled,
@"This callback has been handled by %@.", _debugHandleSource);
331 pendingResponses[@(responseId)] = _callback;
334 ((_debugHandleSource = [NSString stringWithFormat:
@"pending event %llu", responseId]),
TRUE),
338- (void)resolveTo:(
BOOL)handled {
339 NSAssert(!_handled,
@"This callback has been handled by %@.", _debugHandleSource);
345 NSAssert(((_debugHandleSource = [NSString stringWithFormat:
@"resolved with %d", _handled]),
TRUE),
404@property(nonatomic,
copy, readonly)
422- (void)updateKey:(uint64_t)physicalKey asPressed:(uint64_t)logicalKey;
427- (void)synthesizeCapsLockTapWithTimestamp:(NSTimeInterval)timestamp;
447- (void)synthesizeModifierEventOfType:(
BOOL)isDownEvent
448 timestamp:(NSTimeInterval)timestamp
449 keyCode:(UInt32)keyCode;
466- (void)handleResponse:(
BOOL)handled forId:(uint64_t)responseId;
474 withLeftKey:(UInt16)leftKeyCode
475 withRightKey:(UInt16)rightKeyCode
476 withKeyCode:(UInt16)keyCode
477 keyDown:(
BOOL)isKeyDown
478 forFlags:(UInt32)modifiersPressed API_AVAILABLE(
ios(13.4));
493 _sendEvent = [sendEvent copy];
494 _pressingRecords = [[NSMutableDictionary alloc] init];
495 _pendingResponses = [[NSMutableDictionary alloc] init];
497 _lastModifierFlagsOfInterest = 0;
505 if (@available(iOS 13.4, *)) {
511 NSAssert(
callback != nil,
@"The callback must not be nil.");
514 switch (press.phase) {
515 case UIPressPhaseBegan:
517 [
self handlePressBegin:press callback:guardedCallback];
519 case UIPressPhaseEnded:
521 [
self handlePressEnd:press callback:guardedCallback];
523 case UIPressPhaseChanged:
524 case UIPressPhaseCancelled:
526 case UIPressPhaseStationary:
527 NSAssert(
false,
@"Unexpected press phase receieved in handlePress");
530 NSAssert(guardedCallback.
handled,
@"The callback returned without being handled.");
534 @"The modifier flags are not properly updated: recorded 0x%lx, event with mask 0x%lx",
536 static_cast<unsigned long>([
self adjustModifiers:press] &
540#pragma mark - Private
543 if (@available(iOS 13.4, *)) {
548 const UInt32 lastFlagsOfInterest = _lastModifierFlagsOfInterest & _modifierFlagOfInterestMask;
549 const UInt32 pressedModifiers = [
self adjustModifiers:press];
550 const UInt32 currentFlagsOfInterest = pressedModifiers & _modifierFlagOfInterestMask;
551 UInt32 flagDifference = currentFlagsOfInterest ^ lastFlagsOfInterest;
555 if (press.key.keyCode != UIKeyboardHIDUsageKeyboardCapsLock) {
556 [
self synthesizeCapsLockTapWithTimestamp:press.timestamp];
558 flagDifference &= ~kModifierFlagCapsLock;
561 const UInt32 currentFlag =
lowestSetBit(flagDifference);
562 if (currentFlag == 0) {
565 flagDifference &= ~currentFlag;
574 static_cast<unsigned long>(currentFlag));
580 if (keyCode->second ==
static_cast<UInt32
>(press.key.keyCode)) {
583 BOOL isDownEvent = currentFlagsOfInterest & currentFlag;
584 [
self synthesizeModifierEventOfType:isDownEvent
585 timestamp:press.timestamp
586 keyCode:keyCode->second];
588 _lastModifierFlagsOfInterest =
589 (_lastModifierFlagsOfInterest & ~_modifierFlagOfInterestMask) | currentFlagsOfInterest;
592- (void)synthesizeCapsLockTapWithTimestamp:(NSTimeInterval)timestamp {
607 _sendEvent(flutterEvent,
nullptr,
nullptr);
610 _sendEvent(flutterEvent,
nullptr,
nullptr);
613- (void)updateKey:(uint64_t)physicalKey asPressed:(uint64_t)logicalKey {
614 if (logicalKey == 0) {
615 [_pressingRecords removeObjectForKey:@(physicalKey)];
617 _pressingRecords[@(physicalKey)] = @(logicalKey);
639 .synthesized =
false,
642 _sendEvent(
event, nil, nil);
645- (void)synthesizeModifierEventOfType:(
BOOL)isDownEvent
646 timestamp:(NSTimeInterval)timestamp
647 keyCode:(UInt32)keyCode {
650 if (physicalKey == 0 || logicalKey == 0) {
657 .physical = physicalKey,
658 .logical = logicalKey,
663 [
self updateKey:physicalKey asPressed:isDownEvent ? logicalKey : 0];
664 _sendEvent(flutterEvent,
nullptr,
nullptr);
669 if (@available(iOS 13.4, *)) {
677 NSNumber* specialKey = [specialKeyMapping objectForKey:press.key.charactersIgnoringModifiers];
679 [
self synchronizeModifiers:press];
681 NSNumber* pressedLogicalKey = nil;
682 if ([_pressingRecords
count] > 0) {
683 pressedLogicalKey = _pressingRecords[@(physicalKey)];
684 if (pressedLogicalKey != nil) {
690 [
self sendEmptyEvent];
695 if (pressedLogicalKey == nil) {
696 [
self updateKey:physicalKey asPressed:logicalKey];
703 .physical = physicalKey,
704 .logical = pressedLogicalKey == nil ? logicalKey : [pressedLogicalKey unsignedLongLongValue],
706 specialKey != nil ? nil :
getEventCharacters(press.key.characters, press.key.keyCode),
707 .synthesized =
false,
710 [
self sendPrimaryFlutterEvent:flutterEvent callback:callback];
715 if (@available(iOS 13.4, *)) {
719 [
self synchronizeModifiers:press];
722 NSNumber* pressedLogicalKey = _pressingRecords[@(physicalKey)];
723 if (pressedLogicalKey == nil) {
729 [
self sendEmptyEvent];
732 [
self updateKey:physicalKey asPressed:0];
738 .physical = physicalKey,
739 .logical = [pressedLogicalKey unsignedLongLongValue],
741 .synthesized =
false,
744 [
self sendPrimaryFlutterEvent:flutterEvent callback:callback];
747- (void)handleResponse:(
BOOL)handled forId:(uint64_t)responseId {
750 [_pendingResponses removeObjectForKey:@(responseId)];
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;
764 if (keyCode == leftKeyCode) {
765 newModifiers |= leftSide | anyFlag;
766 }
else if (keyCode == rightKeyCode) {
767 newModifiers |= rightSide | anyFlag;
773 if (keyCode == leftKeyCode) {
774 newModifiers &= ~leftSide;
775 if (!(newModifiers & rightSide)) {
776 newModifiers &= ~anyFlag;
778 }
else if (keyCode == rightKeyCode) {
779 newModifiers &= ~rightSide;
780 if (!(newModifiers & leftSide)) {
781 newModifiers &= ~anyFlag;
786 if (!(newModifiers & anyFlag)) {
788 newModifiers &= ~(leftSide | rightSide);
812 if (@available(iOS 13.4, *)) {
815 return press.key.modifierFlags;
822 UInt32 pressedModifiers =
825 pressedModifiers = [
self fixSidedFlags:kModifierFlagShiftAny
826 withLeftFlag:kModifierFlagShiftLeft
827 withRightFlag:kModifierFlagShiftRight
828 withLeftKey:UIKeyboardHIDUsageKeyboardLeftShift
829 withRightKey:UIKeyboardHIDUsageKeyboardRightShift
830 withKeyCode:press.key.keyCode
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
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
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
856 forFlags:pressedModifiers];
858 if (press.key.keyCode == UIKeyboardHIDUsageKeyboardCapsLock) {
867 pressedModifiers &= ~kModifierFlagCapsLock;
870 return pressedModifiers;
878 [pending.responder handleResponse:handled forId:pending.responseId];
static void copy(void *dst, const uint8_t *src, int width, int bpp, int deltaSrc, int offset, const SkPMColor ctable[])
FlutterSendKeyEvent sendEvent
@ kFlutterKeyEventDeviceTypeKeyboard
@ kFlutterKeyEventTypeDown
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
NSUInteger lastModifierFlagsOfInterest
NSMutableDictionary< NSNumber *, NSNumber * > * pressingRecords
NSUInteger modifierFlagOfInterestMask
NSMutableDictionary< NSNumber *, FlutterAsyncKeyCallback > * pendingResponses
NSString * debugHandleSource
void resolveTo:(BOOL handled)
void pendTo:withId:(nonnull NSMutableDictionary< NSNumber *, FlutterAsyncKeyCallback > *pendingResponses,[withId] uint64_t responseId)
FlutterEmbedderKeyResponder * responder
void(^ FlutterSendKeyEvent)(const FlutterKeyEvent &, _Nullable FlutterKeyEventCallback, void *_Nullable)
void(^ FlutterAsyncKeyCallback)(BOOL handled)
const std::map< uint32_t, ModifierFlag > keyCodeToModifierFlag
const std::set< uint32_t > functionKeyCodes
const uint64_t kValueMask
const std::map< uint32_t, uint64_t > keyCodeToPhysicalKey
const uint64_t kUnicodePlane
const std::map< uint32_t, uint64_t > keyCodeToLogicalKey
const uint64_t kCapsLockLogicalKey
const uint64_t kCapsLockPhysicalKey
const std::map< ModifierFlag, uint32_t > modifierFlagToKeyCode
@ kModifierFlagControlAny
constexpr uint32_t kModifierFlagAnyMask
constexpr uint32_t kModifierFlagSidedMask
static uint64_t KeyOfPlane(uint64_t baseKey, uint64_t plane)
static uint64_t toLower(uint64_t n)
static double GetFlutterTimestampFrom(NSTimeInterval timestamp)
static NSUInteger lowestSetBit(NSUInteger bitmask)
static bool isKeyDown(FlutterUIPressProxy *press) API_AVAILABLE(ios(13.4))
static const char * getEventCharacters(NSString *characters, UIKeyboardHIDUsage keyCode) API_AVAILABLE(ios(13.4))
static NSUInteger computeModifierFlagOfInterestMask()
static uint64_t GetPhysicalKeyForKeyCode(UInt32 keyCode)
static bool IsControlCharacter(NSUInteger length, NSString *label)
void HandleResponse(bool handled, void *user_data)
static uint64_t GetLogicalKeyForEvent(FlutterUIPressProxy *press, NSNumber *maybeSpecialKey) API_AVAILABLE(ios(13.4))
static uint64_t GetLogicalKeyForModifier(UInt32 keyCode, uint64_t hidCode)
static bool IsUnprintableKey(NSUInteger length, NSString *label)
SK_API sk_sp< SkSurface > ios(9.0)
API_AVAILABLE(ios(14.0), macos(11.0)) static NSString *MTLCommandEncoderErrorStateToString(MTLCommandEncoderErrorState state)
size_t struct_size
The size of this struct. Must be sizeof(FlutterKeyEvent).
FlutterKeyEventType type
The event kind.