5 #import <objc/message.h>
12 #import "flutter/shell/platform/embedder/embedder.h"
24 static NSUInteger lowestSetBit(NSUInteger bitmask) {
27 return bitmask & -bitmask;
33 static bool IsControlCharacter(uint64_t character) {
34 return (character <= 0x1f && character >= 0x00) || (character >= 0x7f && character <= 0x9f);
40 static bool IsUnprintableKey(uint64_t character) {
41 return character >= 0xF700 && character <= 0xF8FF;
54 static uint64_t KeyOfPlane(uint64_t baseKey, uint64_t plane) {
61 static uint64_t GetPhysicalKeyForKeyCode(
unsigned short keyCode) {
63 if (physicalKey == nil) {
66 return physicalKey.unsignedLongLongValue;
72 static uint64_t GetLogicalKeyForModifier(
unsigned short keyCode, uint64_t hidCode) {
74 if (fromKeyCode != nil) {
75 return fromKeyCode.unsignedLongLongValue;
86 static uint64_t toLower(uint64_t n) {
87 constexpr uint64_t lowerA = 0x61;
88 constexpr uint64_t upperA = 0x41;
89 constexpr uint64_t upperZ = 0x5a;
91 constexpr uint64_t lowerAGrave = 0xe0;
92 constexpr uint64_t upperAGrave = 0xc0;
93 constexpr uint64_t upperThorn = 0xde;
94 constexpr uint64_t division = 0xf7;
97 if (n >= upperA && n <= upperZ) {
98 return n - upperA + lowerA;
102 if (n >= upperAGrave && n <= upperThorn && n != division) {
103 return n - upperAGrave + lowerAGrave;
118 static uint32_t* DecodeUtf16(NSString* target,
size_t* out_length) {
120 size_t result_pos = 0;
121 uint32_t* result =
new uint32_t[target.length];
122 uint16_t high_surrogate = 0;
123 for (NSUInteger target_pos = 0; target_pos < target.length; target_pos += 1) {
124 uint16_t codeUnit = [target characterAtIndex:target_pos];
126 if (codeUnit <= 0xD7FF || codeUnit >= 0xE000) {
127 result[result_pos] = codeUnit;
130 }
else if (codeUnit <= 0xDBFF) {
131 high_surrogate = codeUnit - 0xD800;
134 uint16_t low_surrogate = codeUnit - 0xDC00;
135 result[result_pos] = (high_surrogate << 10) + low_surrogate + 0x10000;
139 *out_length = result_pos;
148 static uint64_t GetLogicalKeyForEvent(NSEvent* event, uint64_t physicalKey) {
151 if (fromKeyCode != nil) {
152 return fromKeyCode.unsignedLongLongValue;
156 NSString* keyLabelUtf16 =
event.charactersIgnoringModifiers;
164 uint32_t character = 0;
165 if (keyLabelUtf16.length != 0) {
166 size_t keyLabelLength;
167 uint32_t* keyLabel = DecodeUtf16(keyLabelUtf16, &keyLabelLength);
168 if (keyLabelLength == 1) {
169 uint32_t keyLabelChar = *keyLabel;
170 NSCAssert(!IsControlCharacter(keyLabelChar) && !IsUnprintableKey(keyLabelChar),
171 @"Unexpected control or unprintable keylabel 0x%x", keyLabelChar);
172 NSCAssert(keyLabelChar <= 0x10FFFF,
@"Out of range keylabel 0x%x", keyLabelChar);
173 character = keyLabelChar;
177 if (character != 0) {
189 static double GetFlutterTimestampFrom(NSTimeInterval timestamp) {
191 return timestamp * 1000000.0;
200 static NSUInteger computeModifierFlagOfInterestMask() {
201 __block NSUInteger modifierFlagOfInterestMask = NSEventModifierFlagCapsLock;
203 enumerateKeysAndObjectsUsingBlock:^(NSNumber* keyCode, NSNumber* flag, BOOL* stop) {
204 modifierFlagOfInterestMask = modifierFlagOfInterestMask | [flag unsignedLongValue];
206 return modifierFlagOfInterestMask;
215 void HandleResponse(
bool handled,
void*
user_data);
220 const char* getEventString(NSString* characters) {
221 if ([characters length] == 0) {
224 unichar utf16Code = [characters characterAtIndex:0];
225 if (utf16Code >= 0xf700 && utf16Code <= 0xf7ff) {
245 return [characters UTF8String];
273 withId:(uint64_t)responseId;
278 - (void)resolveTo:(BOOL)handled;
299 _callback = callback;
301 _sentAnyEvents = FALSE;
307 withId:(uint64_t)responseId {
308 NSAssert(!_handled,
@"This callback has been handled by %@.", _debugHandleSource);
312 pendingResponses[@(responseId)] = _callback;
315 ((_debugHandleSource = [NSString stringWithFormat:
@"pending event %llu", responseId]), TRUE),
319 - (void)resolveTo:(BOOL)handled {
320 NSAssert(!_handled,
@"This callback has been handled by %@.", _debugHandleSource);
326 NSAssert(((_debugHandleSource = [NSString stringWithFormat:
@"resolved with %d", _handled]), TRUE),
346 @property(nonatomic) NSMutableDictionary<NSNumber*, NSNumber*>* pressingRecords;
359 @property(nonatomic) NSUInteger modifierFlagOfInterestMask;
372 @property(nonatomic) NSUInteger lastModifierFlagsOfInterest;
377 @property(nonatomic) uint64_t responseId;
385 @property(nonatomic) NSMutableDictionary<NSNumber*, FlutterAsyncKeyCallback>* pendingResponses;
397 - (void)synchronizeModifiers:(NSUInteger)currentFlags
398 ignoringFlags:(NSUInteger)ignoringFlags
399 timestamp:(NSTimeInterval)timestamp
408 - (void)updateKey:(uint64_t)physicalKey asPressed:(uint64_t)logicalKey;
413 - (void)sendPrimaryFlutterEvent:(const FlutterKeyEvent&)event
422 - (void)sendSynthesizedFlutterEvent:(const FlutterKeyEvent&)event
432 - (void)sendCapsLockTapWithTimestamp:(NSTimeInterval)timestamp
433 synthesizeDown:(
bool)synthesizeDown
439 - (void)sendModifierEventOfType:(BOOL)isDownEvent
440 timestamp:(NSTimeInterval)timestamp
441 keyCode:(
unsigned short)keyCode
442 synthesized:(
bool)synthesized
458 - (void)handleCapsLockEvent:(nonnull NSEvent*)event
469 - (void)handleResponse:(BOOL)handled forId:(uint64_t)responseId;
480 _sendEvent = sendEvent;
481 _pressingRecords = [NSMutableDictionary dictionary];
482 _pendingResponses = [NSMutableDictionary dictionary];
484 _lastModifierFlagsOfInterest = 0;
485 _modifierFlagOfInterestMask = computeModifierFlagOfInterestMask();
493 NSAssert(callback != nil,
@"The callback must not be nil.");
496 switch (event.type) {
497 case NSEventTypeKeyDown:
498 [
self handleDownEvent:event callback:guardedCallback];
500 case NSEventTypeKeyUp:
501 [
self handleUpEvent:event callback:guardedCallback];
503 case NSEventTypeFlagsChanged:
504 [
self handleFlagEvent:event callback:guardedCallback];
507 NSAssert(
false,
@"Unexpected key event type: |%@|.", @(event.type));
509 NSAssert(guardedCallback.
handled,
@"The callback is returned without being handled.");
511 FlutterKeyEvent flutterEvent = {
512 .struct_size =
sizeof(FlutterKeyEvent),
514 .type = kFlutterKeyEventTypeDown,
518 .synthesized =
false,
520 _sendEvent(flutterEvent,
nullptr,
nullptr);
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);
527 #pragma mark - Private
529 - (void)synchronizeModifiers:(NSUInteger)currentFlags
530 ignoringFlags:(NSUInteger)ignoringFlags
531 timestamp:(NSTimeInterval)timestamp
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;
542 const NSUInteger currentFlag = lowestSetBit(flagDifference);
543 if (currentFlag == 0) {
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) {
552 BOOL isDownEvent = (currentFlagsOfInterest & currentFlag) != 0;
553 [
self sendModifierEventOfType:isDownEvent
555 keyCode:[keyCode unsignedShortValue]
559 _lastModifierFlagsOfInterest =
560 (_lastModifierFlagsOfInterest & ~updatingMask) | currentFlagsOfInterest;
563 - (void)updateKey:(uint64_t)physicalKey asPressed:(uint64_t)logicalKey {
564 if (logicalKey == 0) {
565 [_pressingRecords removeObjectForKey:@(physicalKey)];
567 _pressingRecords[@(physicalKey)] = @(logicalKey);
571 - (void)sendPrimaryFlutterEvent:(const FlutterKeyEvent&)event
574 uint64_t responseId = _responseId;
578 _sendEvent(event, HandleResponse, pending);
582 - (void)sendSynthesizedFlutterEvent:(const FlutterKeyEvent&)event
584 _sendEvent(event,
nullptr,
nullptr);
588 - (void)sendCapsLockTapWithTimestamp:(NSTimeInterval)timestamp
589 synthesizeDown:(
bool)synthesizeDown
596 FlutterKeyEvent flutterEvent = {
597 .struct_size =
sizeof(FlutterKeyEvent),
598 .timestamp = GetFlutterTimestampFrom(timestamp),
599 .type = kFlutterKeyEventTypeDown,
603 .synthesized = synthesizeDown,
605 if (!synthesizeDown) {
606 [
self sendPrimaryFlutterEvent:flutterEvent callback:callback];
608 [
self sendSynthesizedFlutterEvent:flutterEvent guard:callback];
611 flutterEvent.type = kFlutterKeyEventTypeUp;
612 flutterEvent.synthesized =
true;
613 [
self sendSynthesizedFlutterEvent:flutterEvent guard:callback];
616 - (void)sendModifierEventOfType:(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);
628 FlutterKeyEvent flutterEvent = {
629 .struct_size =
sizeof(FlutterKeyEvent),
630 .timestamp = GetFlutterTimestampFrom(timestamp),
631 .type = isDownEvent ? kFlutterKeyEventTypeDown : kFlutterKeyEventTypeUp,
632 .physical = physicalKey,
633 .logical = logicalKey,
635 .synthesized = synthesized,
637 [
self updateKey:physicalKey asPressed:isDownEvent ? logicalKey : 0];
639 [
self sendPrimaryFlutterEvent:flutterEvent callback:callback];
641 [
self sendSynthesizedFlutterEvent:flutterEvent guard:callback];
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
652 timestamp:event.timestamp
655 bool isARepeat =
event.isARepeat;
656 NSNumber* pressedLogicalKey = _pressingRecords[@(physicalKey)];
657 if (pressedLogicalKey != nil && !isARepeat) {
664 FlutterKeyEvent flutterEvent = {
665 .struct_size =
sizeof(FlutterKeyEvent),
666 .timestamp = GetFlutterTimestampFrom(event.timestamp),
667 .type = kFlutterKeyEventTypeUp,
668 .physical = physicalKey,
669 .logical = [pressedLogicalKey unsignedLongLongValue],
673 [
self sendSynthesizedFlutterEvent:flutterEvent guard:callback];
674 pressedLogicalKey = nil;
677 if (pressedLogicalKey == nil) {
678 [
self updateKey:physicalKey asPressed:logicalKey];
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,
690 [
self sendPrimaryFlutterEvent:flutterEvent callback:callback];
694 NSAssert(!event.isARepeat,
@"Unexpected repeated Up event: keyCode %d, char %@, charIM %@",
695 event.keyCode, event.characters, event.charactersIgnoringModifiers);
696 [
self synchronizeModifiers:event.modifierFlags
698 timestamp:event.timestamp
701 uint64_t physicalKey = GetPhysicalKeyForKeyCode(event.keyCode);
702 NSNumber* pressedLogicalKey = _pressingRecords[@(physicalKey)];
703 if (pressedLogicalKey == nil) {
711 [
self updateKey:physicalKey asPressed:0];
713 FlutterKeyEvent flutterEvent = {
714 .struct_size =
sizeof(FlutterKeyEvent),
715 .timestamp = GetFlutterTimestampFrom(event.timestamp),
716 .type = kFlutterKeyEventTypeUp,
717 .physical = physicalKey,
718 .logical = [pressedLogicalKey unsignedLongLongValue],
720 .synthesized =
false,
722 [
self sendPrimaryFlutterEvent:flutterEvent callback:callback];
726 [
self synchronizeModifiers:event.modifierFlags
727 ignoringFlags:NSEventModifierFlagCapsLock
728 timestamp:event.timestamp
730 if ((_lastModifierFlagsOfInterest & NSEventModifierFlagCapsLock) !=
731 (
event.modifierFlags & NSEventModifierFlagCapsLock)) {
732 [
self sendCapsLockTapWithTimestamp:event.timestamp synthesizeDown:false callback:callback];
733 _lastModifierFlagsOfInterest = _lastModifierFlagsOfInterest ^ NSEventModifierFlagCapsLock;
741 NSUInteger targetModifierFlag =
742 targetModifierFlagObj == nil ? 0 : [targetModifierFlagObj unsignedLongValue];
743 uint64_t targetKey = GetPhysicalKeyForKeyCode(event.keyCode);
745 return [
self handleCapsLockEvent:event callback:callback];
748 [
self synchronizeModifiers:event.modifierFlags
749 ignoringFlags:targetModifierFlag
750 timestamp:event.timestamp
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,
761 ? [NSString stringWithFormat:
@"0x%llx", [pressedLogicalKey unsignedLongLongValue]]
764 BOOL shouldBePressed = (
event.modifierFlags & targetModifierFlag) != 0;
765 if (lastTargetPressed == shouldBePressed) {
769 _lastModifierFlagsOfInterest = _lastModifierFlagsOfInterest ^ targetModifierFlag;
770 [
self sendModifierEventOfType:shouldBePressed
771 timestamp:event.timestamp
772 keyCode:event.keyCode
777 - (void)handleResponse:(BOOL)handled forId:(uint64_t)responseId {
780 [_pendingResponses removeObjectForKey:@(responseId)];
783 - (void)syncModifiersIfNeeded:(NSEventModifierFlags)modifierFlags
784 timestamp:(NSTimeInterval)timestamp {
790 [
self synchronizeModifiers:modifierFlags
793 guard:guardedCallback];
797 return [NSDictionary dictionaryWithDictionary:_pressingRecords];
802 void HandleResponse(
bool handled,
void*
user_data) {
804 auto pending = std::unique_ptr<FlutterKeyPendingResponse>(
806 [pending->responder handleResponse:handled forId:pending->responseId];