386@property(nonatomic) NSMutableDictionary<NSNumber*, FlutterAsyncKeyCallback>* pendingResponses;
398- (void)synchronizeModifiers:(NSUInteger)currentFlags
399 ignoringFlags:(NSUInteger)ignoringFlags
400 timestamp:(NSTimeInterval)timestamp
409- (void)updateKey:(uint64_t)physicalKey asPressed:(uint64_t)logicalKey;
433- (void)sendCapsLockTapWithTimestamp:(NSTimeInterval)timestamp
434 synthesizeDown:(
bool)synthesizeDown
440- (void)sendModifierEventOfType:(
BOOL)isDownEvent
441 timestamp:(NSTimeInterval)timestamp
442 keyCode:(
unsigned short)keyCode
443 synthesized:(
bool)synthesized
459- (void)handleCapsLockEvent:(nonnull NSEvent*)event
470- (void)handleResponse:(
BOOL)handled forId:(uint64_t)responseId;
482 _sendEvent = sendEvent;
483 _pressingRecords = [NSMutableDictionary dictionary];
484 _pendingResponses = [NSMutableDictionary dictionary];
486 _lastModifierFlagsOfInterest = 0;
487 _modifierFlagOfInterestMask = computeModifierFlagOfInterestMask();
495 NSAssert(
callback != nil,
@"The callback must not be nil.");
498 switch (event.type) {
499 case NSEventTypeKeyDown:
500 [
self handleDownEvent:event callback:guardedCallback];
502 case NSEventTypeKeyUp:
503 [
self handleUpEvent:event callback:guardedCallback];
505 case NSEventTypeFlagsChanged:
506 [
self handleFlagEvent:event callback:guardedCallback];
509 NSAssert(
false,
@"Unexpected key event type: |%@|.", @(event.type));
511 NSAssert(guardedCallback.
handled,
@"The callback is returned without being handled.");
520 .synthesized =
false,
522 _sendEvent(flutterEvent,
nullptr,
nullptr);
524 NSAssert(_lastModifierFlagsOfInterest == (event.modifierFlags & _modifierFlagOfInterestMask),
525 @"The modifier flags are not properly updated: recorded 0x%lx, event with mask 0x%lx",
526 _lastModifierFlagsOfInterest, event.modifierFlags & _modifierFlagOfInterestMask);
529#pragma mark - Private
531- (void)synchronizeModifiers:(NSUInteger)currentFlags
532 ignoringFlags:(NSUInteger)ignoringFlags
533 timestamp:(NSTimeInterval)timestamp
535 const NSUInteger updatingMask = _modifierFlagOfInterestMask & ~ignoringFlags;
536 const NSUInteger currentFlagsOfInterest = currentFlags & updatingMask;
537 const NSUInteger lastFlagsOfInterest = _lastModifierFlagsOfInterest & updatingMask;
538 NSUInteger flagDifference = currentFlagsOfInterest ^ lastFlagsOfInterest;
539 if (flagDifference & NSEventModifierFlagCapsLock) {
540 [
self sendCapsLockTapWithTimestamp:timestamp synthesizeDown:true callback:guard];
541 flagDifference = flagDifference & ~NSEventModifierFlagCapsLock;
544 const NSUInteger currentFlag = lowestSetBit(flagDifference);
545 if (currentFlag == 0) {
548 flagDifference = flagDifference & ~currentFlag;
549 NSNumber* keyCode = [flutter::modifierFlagToKeyCode objectForKey:@(currentFlag)];
550 NSAssert(keyCode != nil,
@"Invalid modifier flag 0x%lx", currentFlag);
551 if (keyCode == nil) {
554 BOOL isDownEvent = (currentFlagsOfInterest & currentFlag) != 0;
555 [
self sendModifierEventOfType:isDownEvent
557 keyCode:[keyCode unsignedShortValue]
561 _lastModifierFlagsOfInterest =
562 (_lastModifierFlagsOfInterest & ~updatingMask) | currentFlagsOfInterest;
565- (void)updateKey:(uint64_t)physicalKey asPressed:(uint64_t)logicalKey {
566 if (logicalKey == 0) {
567 [_pressingRecords removeObjectForKey:@(physicalKey)];
569 _pressingRecords[@(physicalKey)] = @(logicalKey);
576 uint64_t responseId = _responseId;
580 _sendEvent(event, HandleResponse, pending);
586 _sendEvent(event,
nullptr,
nullptr);
590- (void)sendCapsLockTapWithTimestamp:(NSTimeInterval)timestamp
591 synthesizeDown:(
bool)synthesizeDown
600 .timestamp = GetFlutterTimestampFrom(timestamp),
605 .synthesized = synthesizeDown,
607 if (!synthesizeDown) {
608 [
self sendPrimaryFlutterEvent:flutterEvent callback:callback];
610 [
self sendSynthesizedFlutterEvent:flutterEvent guard:callback];
615 [
self sendSynthesizedFlutterEvent:flutterEvent guard:callback];
618- (void)sendModifierEventOfType:(
BOOL)isDownEvent
619 timestamp:(NSTimeInterval)timestamp
620 keyCode:(
unsigned short)keyCode
621 synthesized:(
bool)synthesized
623 uint64_t physicalKey = GetPhysicalKeyForKeyCode(keyCode);
624 uint64_t logicalKey = GetLogicalKeyForModifier(keyCode, physicalKey);
625 if (physicalKey == 0 || logicalKey == 0) {
626 NSLog(
@"Unrecognized modifier key: keyCode 0x%hx, physical key 0x%llx", keyCode, physicalKey);
632 .timestamp = GetFlutterTimestampFrom(timestamp),
634 .physical = physicalKey,
635 .logical = logicalKey,
637 .synthesized = synthesized,
639 [
self updateKey:physicalKey asPressed:isDownEvent ? logicalKey : 0];
641 [
self sendPrimaryFlutterEvent:flutterEvent callback:callback];
643 [
self sendSynthesizedFlutterEvent:flutterEvent guard:callback];
648 uint64_t physicalKey = GetPhysicalKeyForKeyCode(event.keyCode);
649 NSNumber* logicalKeyFromMap =
self.layoutMap[@(event.keyCode)];
650 uint64_t logicalKey = logicalKeyFromMap != nil ? [logicalKeyFromMap unsignedLongLongValue]
651 : GetLogicalKeyForEvent(event, physicalKey);
652 [
self synchronizeModifiers:event.modifierFlags
654 timestamp:event.timestamp
657 bool isARepeat =
event.isARepeat;
658 NSNumber* pressedLogicalKey = _pressingRecords[@(physicalKey)];
659 if (pressedLogicalKey != nil && !isARepeat) {
668 .timestamp = GetFlutterTimestampFrom(event.timestamp),
670 .physical = physicalKey,
671 .logical = [pressedLogicalKey unsignedLongLongValue],
675 [
self sendSynthesizedFlutterEvent:flutterEvent guard:callback];
676 pressedLogicalKey = nil;
679 if (pressedLogicalKey == nil) {
680 [
self updateKey:physicalKey asPressed:logicalKey];
685 .timestamp = GetFlutterTimestampFrom(event.timestamp),
687 .physical = physicalKey,
688 .logical = pressedLogicalKey == nil ? logicalKey : [pressedLogicalKey unsignedLongLongValue],
689 .character = getEventString(event.characters),
690 .synthesized =
false,
692 [
self sendPrimaryFlutterEvent:flutterEvent callback:callback];
696 NSAssert(!event.isARepeat,
@"Unexpected repeated Up event: keyCode %d, char %@, charIM %@",
697 event.keyCode, event.characters, event.charactersIgnoringModifiers);
698 [
self synchronizeModifiers:event.modifierFlags
700 timestamp:event.timestamp
703 uint64_t physicalKey = GetPhysicalKeyForKeyCode(event.keyCode);
704 NSNumber* pressedLogicalKey = _pressingRecords[@(physicalKey)];
705 if (pressedLogicalKey == nil) {
713 [
self updateKey:physicalKey asPressed:0];
717 .timestamp = GetFlutterTimestampFrom(event.timestamp),
719 .physical = physicalKey,
720 .logical = [pressedLogicalKey unsignedLongLongValue],
722 .synthesized =
false,
724 [
self sendPrimaryFlutterEvent:flutterEvent callback:callback];
728 [
self synchronizeModifiers:event.modifierFlags
729 ignoringFlags:NSEventModifierFlagCapsLock
730 timestamp:event.timestamp
732 if ((_lastModifierFlagsOfInterest & NSEventModifierFlagCapsLock) !=
733 (
event.modifierFlags & NSEventModifierFlagCapsLock)) {
734 [
self sendCapsLockTapWithTimestamp:event.timestamp synthesizeDown:false callback:callback];
735 _lastModifierFlagsOfInterest = _lastModifierFlagsOfInterest ^ NSEventModifierFlagCapsLock;
743 NSUInteger targetModifierFlag =
744 targetModifierFlagObj == nil ? 0 : [targetModifierFlagObj unsignedLongValue];
745 uint64_t targetKey = GetPhysicalKeyForKeyCode(event.keyCode);
747 return [
self handleCapsLockEvent:event callback:callback];
750 [
self synchronizeModifiers:event.modifierFlags
751 ignoringFlags:targetModifierFlag
752 timestamp:event.timestamp
755 NSNumber* pressedLogicalKey = [_pressingRecords objectForKey:@(targetKey)];
756 BOOL lastTargetPressed = pressedLogicalKey != nil;
757 NSAssert(targetModifierFlagObj == nil ||
758 (_lastModifierFlagsOfInterest & targetModifierFlag) != 0 == lastTargetPressed,
759 @"Desynchronized state between lastModifierFlagsOfInterest (0x%lx) on bit 0x%lx "
760 @"for keyCode 0x%hx, whose pressing state is %@.",
761 _lastModifierFlagsOfInterest, targetModifierFlag, event.keyCode,
763 ? [NSString stringWithFormat:
@"0x%llx", [pressedLogicalKey unsignedLongLongValue]]
766 BOOL shouldBePressed = (
event.modifierFlags & targetModifierFlag) != 0;
767 if (lastTargetPressed == shouldBePressed) {
771 _lastModifierFlagsOfInterest = _lastModifierFlagsOfInterest ^ targetModifierFlag;
772 [
self sendModifierEventOfType:shouldBePressed
773 timestamp:event.timestamp
774 keyCode:event.keyCode
779- (void)handleResponse:(
BOOL)handled forId:(uint64_t)responseId {
782 [_pendingResponses removeObjectForKey:@(responseId)];
785- (void)syncModifiersIfNeeded:(NSEventModifierFlags)modifierFlags
786 timestamp:(NSTimeInterval)timestamp {
792 [
self synchronizeModifiers:modifierFlags
795 guard:guardedCallback];
799 return [NSDictionary dictionaryWithDictionary:_pressingRecords];