6 #include <objc/NSObjCRuntime.h>
8 #import <objc/message.h>
10 #include "fml/memory/weak_ptr.h"
25 static NSUInteger lowestSetBit(NSUInteger bitmask) {
28 return bitmask & -bitmask;
34 static bool IsControlCharacter(NSUInteger length, NSString* label) {
38 unichar codeUnit = [label characterAtIndex:0];
39 return (codeUnit <= 0x1f && codeUnit >= 0x00) || (codeUnit >= 0x7f && codeUnit <= 0x9f);
45 static bool IsUnprintableKey(NSUInteger length, NSString* label) {
49 unichar codeUnit = [label characterAtIndex:0];
50 return codeUnit >= 0xF700 && codeUnit <= 0xF8FF;
63 static uint64_t KeyOfPlane(uint64_t baseKey, uint64_t plane) {
70 static uint64_t GetPhysicalKeyForKeyCode(UInt32 keyCode) {
75 return physicalKey->second;
81 static uint64_t GetLogicalKeyForModifier(UInt32 keyCode, uint64_t hidCode) {
84 return fromKeyCode->second;
95 static uint64_t toLower(uint64_t n) {
96 constexpr uint64_t lower_a = 0x61;
97 constexpr uint64_t upper_a = 0x41;
98 constexpr uint64_t upper_z = 0x5a;
100 constexpr uint64_t lower_a_grave = 0xe0;
101 constexpr uint64_t upper_a_grave = 0xc0;
102 constexpr uint64_t upper_thorn = 0xde;
103 constexpr uint64_t division = 0xf7;
106 if (n >= upper_a && n <= upper_z) {
107 return n - upper_a + lower_a;
111 if (n >= upper_a_grave && n <= upper_thorn && n != division) {
112 return n - upper_a_grave + lower_a_grave;
121 static const char* getEventCharacters(NSString* characters, UIKeyboardHIDUsage keyCode)
123 if (characters == nil) {
126 if ([characters length] == 0) {
129 if (@available(iOS 13.4, *)) {
143 return [characters UTF8String];
155 static uint64_t GetLogicalKeyForEvent(
FlutterUIPressProxy* press, NSNumber* maybeSpecialKey)
157 if (maybeSpecialKey != nil) {
158 return [maybeSpecialKey unsignedLongLongValue];
163 return fromKeyCode->second;
165 const char* characters =
166 getEventCharacters(press.key.charactersIgnoringModifiers, press.key.keyCode);
168 characters ==
nullptr ? nil : [[[NSString alloc] initWithUTF8String:characters] autorelease];
169 NSUInteger keyLabelLength = [keyLabel length];
174 if (keyLabelLength != 0 && !IsControlCharacter(keyLabelLength, keyLabel) &&
175 !IsUnprintableKey(keyLabelLength, keyLabel)) {
180 NSCAssert((keyLabelLength < 2),
@"Unexpected long key label: |%@|.", keyLabel);
182 uint64_t codeUnit = (uint64_t)[keyLabel characterAtIndex:0];
183 if (keyLabelLength == 2) {
184 uint64_t secondCode = (uint64_t)[keyLabel characterAtIndex:1];
185 codeUnit = (codeUnit << 16) | secondCode;
192 return KeyOfPlane(press.key.keyCode,
kIosPlane);
198 static double GetFlutterTimestampFrom(NSTimeInterval timestamp) {
200 return timestamp * 1000000.0;
208 static NSUInteger computeModifierFlagOfInterestMask() {
213 modifierFlagOfInterestMask = modifierFlagOfInterestMask | entry.second;
215 return modifierFlagOfInterestMask;
219 switch (press.phase) {
220 case UIPressPhaseStationary:
221 case UIPressPhaseChanged:
225 case UIPressPhaseBegan:
227 case UIPressPhaseCancelled:
228 case UIPressPhaseEnded:
240 void HandleResponse(
bool handled,
void*
user_data);
260 responseId:(uint64_t)responseId;
266 responseId:(uint64_t)responseId {
291 withId:(uint64_t)responseId;
296 - (void)resolveTo:(BOOL)handled;
316 _callback = [callback copy];
328 withId:(uint64_t)responseId {
329 NSAssert(!_handled,
@"This callback has been handled by %@.", _debugHandleSource);
333 pendingResponses[@(responseId)] = _callback;
336 ((_debugHandleSource = [NSString stringWithFormat:
@"pending event %llu", responseId]), TRUE),
340 - (void)resolveTo:(BOOL)handled {
341 NSAssert(!_handled,
@"This callback has been handled by %@.", _debugHandleSource);
347 NSAssert(((_debugHandleSource = [NSString stringWithFormat:
@"resolved with %d", _handled]), TRUE),
367 @property(nonatomic, retain, readonly) NSMutableDictionary<NSNumber*, NSNumber*>* pressingRecords;
380 @property(nonatomic) NSUInteger modifierFlagOfInterestMask;
393 @property(nonatomic) NSUInteger lastModifierFlagsOfInterest;
398 @property(nonatomic) uint64_t responseId;
406 @property(nonatomic, retain, readonly)
407 NSMutableDictionary<NSNumber*, FlutterAsyncKeyCallback>* pendingResponses;
424 - (void)updateKey:(uint64_t)physicalKey asPressed:(uint64_t)logicalKey;
429 - (void)synthesizeCapsLockTapWithTimestamp:(NSTimeInterval)timestamp;
434 - (void)sendPrimaryFlutterEvent:(const FlutterKeyEvent&)event
444 - (void)sendEmptyEvent;
449 - (void)synthesizeModifierEventOfType:(BOOL)isDownEvent
450 timestamp:(NSTimeInterval)timestamp
451 keyCode:(UInt32)keyCode;
468 - (void)handleResponse:(BOOL)handled forId:(uint64_t)responseId;
476 withLeftKey:(UInt16)leftKeyCode
477 withRightKey:(UInt16)rightKeyCode
478 withKeyCode:(UInt16)keyCode
479 keyDown:(BOOL)isKeyDown
480 forFlags:(UInt32)modifiersPressed API_AVAILABLE(ios(13.4));
495 _sendEvent = [sendEvent copy];
496 _pressingRecords = [[NSMutableDictionary alloc] init];
497 _pendingResponses = [[NSMutableDictionary alloc] init];
499 _lastModifierFlagsOfInterest = 0;
500 _modifierFlagOfInterestMask = computeModifierFlagOfInterestMask();
506 [_sendEvent release];
507 [_pressingRecords release];
508 [_pendingResponses release];
514 if (@available(iOS 13.4, *)) {
520 NSAssert(callback != nil,
@"The callback must not be nil.");
523 switch (press.phase) {
524 case UIPressPhaseBegan:
526 [
self handlePressBegin:press callback:guardedCallback];
528 case UIPressPhaseEnded:
530 [
self handlePressEnd:press callback:guardedCallback];
532 case UIPressPhaseChanged:
533 case UIPressPhaseCancelled:
535 case UIPressPhaseStationary:
536 NSAssert(
false,
@"Unexpected press phase receieved in handlePress");
539 NSAssert(guardedCallback.
handled,
@"The callback returned without being handled.");
543 @"The modifier flags are not properly updated: recorded 0x%lx, event with mask 0x%lx",
545 static_cast<unsigned long>([
self adjustModifiers:press] &
549 #pragma mark - Private
551 - (void)synchronizeModifiers:(nonnull
FlutterUIPressProxy*)press API_AVAILABLE(ios(13.4)) {
552 if (@available(iOS 13.4, *)) {
557 const UInt32 lastFlagsOfInterest = _lastModifierFlagsOfInterest & _modifierFlagOfInterestMask;
558 const UInt32 pressedModifiers = [
self adjustModifiers:press];
559 const UInt32 currentFlagsOfInterest = pressedModifiers & _modifierFlagOfInterestMask;
560 UInt32 flagDifference = currentFlagsOfInterest ^ lastFlagsOfInterest;
564 if (press.key.keyCode != UIKeyboardHIDUsageKeyboardCapsLock) {
565 [
self synthesizeCapsLockTapWithTimestamp:press.timestamp];
570 const UInt32 currentFlag = lowestSetBit(flagDifference);
571 if (currentFlag == 0) {
574 flagDifference &= ~currentFlag;
583 static_cast<unsigned long>(currentFlag));
589 if (keyCode->second ==
static_cast<UInt32
>(press.key.keyCode)) {
592 BOOL isDownEvent = currentFlagsOfInterest & currentFlag;
593 [
self synthesizeModifierEventOfType:isDownEvent
594 timestamp:press.timestamp
595 keyCode:keyCode->second];
597 _lastModifierFlagsOfInterest =
598 (_lastModifierFlagsOfInterest & ~_modifierFlagOfInterestMask) | currentFlagsOfInterest;
601 - (void)synthesizeCapsLockTapWithTimestamp:(NSTimeInterval)timestamp {
606 FlutterKeyEvent flutterEvent = {
607 .struct_size =
sizeof(FlutterKeyEvent),
608 .timestamp = GetFlutterTimestampFrom(timestamp),
609 .type = kFlutterKeyEventTypeDown,
614 .device_type = kFlutterKeyEventDeviceTypeKeyboard,
616 _sendEvent(flutterEvent,
nullptr,
nullptr);
618 flutterEvent.type = kFlutterKeyEventTypeUp;
619 _sendEvent(flutterEvent,
nullptr,
nullptr);
622 - (void)updateKey:(uint64_t)physicalKey asPressed:(uint64_t)logicalKey {
623 if (logicalKey == 0) {
624 [_pressingRecords removeObjectForKey:@(physicalKey)];
626 _pressingRecords[@(physicalKey)] = @(logicalKey);
630 - (void)sendPrimaryFlutterEvent:(const FlutterKeyEvent&)event
633 uint64_t responseId = _responseId;
637 _sendEvent(event, HandleResponse, pending);
640 - (void)sendEmptyEvent {
641 FlutterKeyEvent
event = {
642 .struct_size =
sizeof(FlutterKeyEvent),
644 .type = kFlutterKeyEventTypeDown,
648 .synthesized =
false,
649 .device_type = kFlutterKeyEventDeviceTypeKeyboard,
651 _sendEvent(event, nil, nil);
654 - (void)synthesizeModifierEventOfType:(BOOL)isDownEvent
655 timestamp:(NSTimeInterval)timestamp
656 keyCode:(UInt32)keyCode {
657 uint64_t physicalKey = GetPhysicalKeyForKeyCode(keyCode);
658 uint64_t logicalKey = GetLogicalKeyForModifier(keyCode, physicalKey);
659 if (physicalKey == 0 || logicalKey == 0) {
662 FlutterKeyEvent flutterEvent = {
663 .struct_size =
sizeof(FlutterKeyEvent),
664 .timestamp = GetFlutterTimestampFrom(timestamp),
665 .type = isDownEvent ? kFlutterKeyEventTypeDown : kFlutterKeyEventTypeUp,
666 .physical = physicalKey,
667 .logical = logicalKey,
670 .device_type = kFlutterKeyEventDeviceTypeKeyboard,
672 [
self updateKey:physicalKey asPressed:isDownEvent ? logicalKey : 0];
673 _sendEvent(flutterEvent,
nullptr,
nullptr);
678 if (@available(iOS 13.4, *)) {
682 uint64_t physicalKey = GetPhysicalKeyForKeyCode(press.key.keyCode);
686 NSNumber* specialKey = [specialKeyMapping objectForKey:press.key.charactersIgnoringModifiers];
687 uint64_t logicalKey = GetLogicalKeyForEvent(press, specialKey);
688 [
self synchronizeModifiers:press];
690 NSNumber* pressedLogicalKey = nil;
691 if ([_pressingRecords count] > 0) {
692 pressedLogicalKey = _pressingRecords[@(physicalKey)];
693 if (pressedLogicalKey != nil) {
699 [
self sendEmptyEvent];
704 if (pressedLogicalKey == nil) {
705 [
self updateKey:physicalKey asPressed:logicalKey];
708 FlutterKeyEvent flutterEvent = {
709 .struct_size =
sizeof(FlutterKeyEvent),
710 .timestamp = GetFlutterTimestampFrom(press.timestamp),
711 .type = kFlutterKeyEventTypeDown,
712 .physical = physicalKey,
713 .logical = pressedLogicalKey == nil ? logicalKey : [pressedLogicalKey unsignedLongLongValue],
715 specialKey != nil ? nil : getEventCharacters(press.key.characters, press.key.keyCode),
716 .synthesized =
false,
717 .device_type = kFlutterKeyEventDeviceTypeKeyboard,
719 [
self sendPrimaryFlutterEvent:flutterEvent callback:callback];
724 if (@available(iOS 13.4, *)) {
728 [
self synchronizeModifiers:press];
730 uint64_t physicalKey = GetPhysicalKeyForKeyCode(press.key.keyCode);
731 NSNumber* pressedLogicalKey = _pressingRecords[@(physicalKey)];
732 if (pressedLogicalKey == nil) {
738 [
self sendEmptyEvent];
741 [
self updateKey:physicalKey asPressed:0];
743 FlutterKeyEvent flutterEvent = {
744 .struct_size =
sizeof(FlutterKeyEvent),
745 .timestamp = GetFlutterTimestampFrom(press.timestamp),
746 .type = kFlutterKeyEventTypeUp,
747 .physical = physicalKey,
748 .logical = [pressedLogicalKey unsignedLongLongValue],
750 .synthesized =
false,
751 .device_type = kFlutterKeyEventDeviceTypeKeyboard,
753 [
self sendPrimaryFlutterEvent:flutterEvent callback:callback];
756 - (void)handleResponse:(BOOL)handled forId:(uint64_t)responseId {
759 [_pendingResponses removeObjectForKey:@(responseId)];
765 withLeftKey:(UInt16)leftKeyCode
766 withRightKey:(UInt16)rightKeyCode
767 withKeyCode:(UInt16)keyCode
768 keyDown:(BOOL)isKeyDown
769 forFlags:(UInt32)modifiersPressed API_AVAILABLE(ios(13.4)) {
770 UInt32 newModifiers = modifiersPressed;
773 if (keyCode == leftKeyCode) {
774 newModifiers |= leftSide | anyFlag;
775 }
else if (keyCode == rightKeyCode) {
776 newModifiers |= rightSide | anyFlag;
782 if (keyCode == leftKeyCode) {
783 newModifiers &= ~leftSide;
784 if (!(newModifiers & rightSide)) {
785 newModifiers &= ~anyFlag;
787 }
else if (keyCode == rightKeyCode) {
788 newModifiers &= ~rightSide;
789 if (!(newModifiers & leftSide)) {
790 newModifiers &= ~anyFlag;
795 if (!(newModifiers & anyFlag)) {
797 newModifiers &= ~(leftSide | rightSide);
821 if (@available(iOS 13.4, *)) {
824 return press.key.modifierFlags;
827 bool keyDown = isKeyDown(press);
831 UInt32 pressedModifiers =
834 pressedModifiers = [
self fixSidedFlags:kModifierFlagShiftAny
835 withLeftFlag:kModifierFlagShiftLeft
836 withRightFlag:kModifierFlagShiftRight
837 withLeftKey:UIKeyboardHIDUsageKeyboardLeftShift
838 withRightKey:UIKeyboardHIDUsageKeyboardRightShift
839 withKeyCode:press.key.keyCode
841 forFlags:pressedModifiers];
842 pressedModifiers = [
self fixSidedFlags:kModifierFlagControlAny
843 withLeftFlag:kModifierFlagControlLeft
844 withRightFlag:kModifierFlagControlRight
845 withLeftKey:UIKeyboardHIDUsageKeyboardLeftControl
846 withRightKey:UIKeyboardHIDUsageKeyboardRightControl
847 withKeyCode:press.key.keyCode
849 forFlags:pressedModifiers];
850 pressedModifiers = [
self fixSidedFlags:kModifierFlagAltAny
851 withLeftFlag:kModifierFlagAltLeft
852 withRightFlag:kModifierFlagAltRight
853 withLeftKey:UIKeyboardHIDUsageKeyboardLeftAlt
854 withRightKey:UIKeyboardHIDUsageKeyboardRightAlt
855 withKeyCode:press.key.keyCode
857 forFlags:pressedModifiers];
858 pressedModifiers = [
self fixSidedFlags:kModifierFlagMetaAny
859 withLeftFlag:kModifierFlagMetaLeft
860 withRightFlag:kModifierFlagMetaRight
861 withLeftKey:UIKeyboardHIDUsageKeyboardLeftGUI
862 withRightKey:UIKeyboardHIDUsageKeyboardRightGUI
863 withKeyCode:press.key.keyCode
865 forFlags:pressedModifiers];
867 if (press.key.keyCode == UIKeyboardHIDUsageKeyboardCapsLock) {
879 return pressedModifiers;
885 void HandleResponse(
bool handled,
void*
user_data) {
887 [pending.responder handleResponse:handled forId:pending.responseId];