Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -146,10 +146,17 @@ static uint64_t toLower(uint64_t n) {
/**
* Returns the logical key of a KeyUp or KeyDown event.
*
* The `maybeSpecialKey` is a nullable integer, and if not nil, indicates
* that the event key is a special key as defined by `specialKeyMapping`,
* and is the corresponding logical key.
*
* For modifier keys, use GetLogicalKeyForModifier.
*/
static uint64_t GetLogicalKeyForEvent(FlutterUIPressProxy* press, uint64_t physicalKey)
static uint64_t GetLogicalKeyForEvent(FlutterUIPressProxy* press, NSNumber* maybeSpecialKey)
API_AVAILABLE(ios(13.4)) {
if (maybeSpecialKey != nil) {
return [maybeSpecialKey unsignedLongLongValue];
}
// Look to see if the keyCode can be mapped from keycode.
auto fromKeyCode = keyCodeToLogicalKey.find(press.key.keyCode);
if (fromKeyCode != keyCodeToLogicalKey.end()) {
Expand Down Expand Up @@ -670,7 +677,11 @@ - (void)handlePressBegin:(nonnull FlutterUIPressProxy*)press
return;
}
uint64_t physicalKey = GetPhysicalKeyForKeyCode(press.key.keyCode);
uint64_t logicalKey = GetLogicalKeyForEvent(press, physicalKey);
// Some unprintable keys on iOS have literal names on their key label, such as
// @"UIKeyInputEscape". They are called the "special keys" and have predefined
// logical keys and empty characters.
NSNumber* specialKey = [specialKeyMapping objectForKey:press.key.charactersIgnoringModifiers];
uint64_t logicalKey = GetLogicalKeyForEvent(press, specialKey);
[self synchronizeModifiers:press];

NSNumber* pressedLogicalKey = nil;
Expand All @@ -697,7 +708,8 @@ - (void)handlePressBegin:(nonnull FlutterUIPressProxy*)press
.type = kFlutterKeyEventTypeDown,
.physical = physicalKey,
.logical = pressedLogicalKey == nil ? logicalKey : [pressedLogicalKey unsignedLongLongValue],
.character = getEventCharacters(press.key.characters, press.key.keyCode),
.character =
specialKey != nil ? nil : getEventCharacters(press.key.characters, press.key.keyCode),
.synthesized = false,
};
[self sendPrimaryFlutterEvent:flutterEvent callback:callback];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ - (void)dealloc {
API_AVAILABLE(ios(13.4))
constexpr UIKeyboardHIDUsage kKeyCodeKeyA = (UIKeyboardHIDUsage)0x04;
API_AVAILABLE(ios(13.4))
constexpr UIKeyboardHIDUsage kKeyCodePeriod = (UIKeyboardHIDUsage)0x37;
API_AVAILABLE(ios(13.4))
constexpr UIKeyboardHIDUsage kKeyCodeKeyW = (UIKeyboardHIDUsage)0x1a;
API_AVAILABLE(ios(13.4))
constexpr UIKeyboardHIDUsage kKeyCodeShiftLeft = (UIKeyboardHIDUsage)0xe1;
Expand All @@ -87,6 +89,8 @@ - (void)dealloc {
API_AVAILABLE(ios(13.4))
constexpr UIKeyboardHIDUsage kKeyCodeF1 = (UIKeyboardHIDUsage)0x3a;
API_AVAILABLE(ios(13.4))
constexpr UIKeyboardHIDUsage kKeyCodeCommandLeft = (UIKeyboardHIDUsage)0xe3;
API_AVAILABLE(ios(13.4))
constexpr UIKeyboardHIDUsage kKeyCodeAltRight = (UIKeyboardHIDUsage)0xe6;
API_AVAILABLE(ios(13.4))
constexpr UIKeyboardHIDUsage kKeyCodeEject = (UIKeyboardHIDUsage)0xb8;
Expand Down Expand Up @@ -941,4 +945,69 @@ - (void)testSynchronizeCapsLockStateOnNormalKey API_AVAILABLE(ios(13.4)) {
[events removeAllObjects];
}

// Press Cmd-. should correctly result in an Escape event.
- (void)testCommandPeriodKey API_AVAILABLE(ios(13.4)) {
__block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
id keyEventCallback = ^(BOOL handled) {
};
FlutterKeyEvent* event;

FlutterEmbedderKeyResponder* responder = [[FlutterEmbedderKeyResponder alloc]
initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
_Nullable _VoidPtr user_data) {
[events addObject:[[TestKeyEvent alloc] initWithEvent:&event callback:nil userData:nil]];
callback(true, user_data);
}];

// MetaLeft down.
[responder handlePress:keyDownEvent(kKeyCodeCommandLeft, kModifierFlagMetaAny, 123.0f, "", "")
callback:keyEventCallback];
XCTAssertEqual([events count], 1u);
event = events[0].data;
XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
XCTAssertEqual(event->physical, kPhysicalMetaLeft);
XCTAssertEqual(event->logical, kLogicalMetaLeft);
XCTAssertEqual(event->character, nullptr);
XCTAssertEqual(event->synthesized, false);
[events removeAllObjects];

// Period down, which is logically Escape.
[responder handlePress:keyDownEvent(kKeyCodePeriod, kModifierFlagMetaAny, 123.0f,
"UIKeyInputEscape", "UIKeyInputEscape")
callback:keyEventCallback];
XCTAssertEqual([events count], 1u);
event = events[0].data;
XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
XCTAssertEqual(event->physical, kPhysicalPeriod);
XCTAssertEqual(event->logical, kLogicalEscape);
XCTAssertEqual(event->character, nullptr);
XCTAssertEqual(event->synthesized, false);
[events removeAllObjects];

// Period up, which unconventionally has characters.
[responder handlePress:keyUpEvent(kKeyCodePeriod, kModifierFlagMetaAny, 123.0f,
"UIKeyInputEscape", "UIKeyInputEscape")
callback:keyEventCallback];
XCTAssertEqual([events count], 1u);
event = events[0].data;
XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
XCTAssertEqual(event->physical, kPhysicalPeriod);
XCTAssertEqual(event->logical, kLogicalEscape);
XCTAssertEqual(event->character, nullptr);
XCTAssertEqual(event->synthesized, false);
[events removeAllObjects];

// MetaLeft up.
[responder handlePress:keyUpEvent(kKeyCodeCommandLeft, kModifierFlagMetaAny, 123.0f, "", "")
callback:keyEventCallback];
XCTAssertEqual([events count], 1u);
event = events[0].data;
XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
XCTAssertEqual(event->physical, kPhysicalMetaLeft);
XCTAssertEqual(event->logical, kLogicalMetaLeft);
XCTAssertEqual(event->character, nullptr);
XCTAssertEqual(event->synthesized, false);
[events removeAllObjects];
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@ FlutterUIPressProxy* keyDownEvent(UIKeyboardHIDUsage keyCode,

FlutterUIPressProxy* keyUpEvent(UIKeyboardHIDUsage keyCode,
UIKeyModifierFlags modifierFlags = 0x0,
NSTimeInterval timestamp = 0.0f) API_AVAILABLE(ios(13.4));
NSTimeInterval timestamp = 0.0f,
const char* characters = "",
const char* charactersIgnoringModifiers = "")
API_AVAILABLE(ios(13.4));

FlutterUIPressProxy* keyEventWithPhase(UIPressPhase phase,
UIKeyboardHIDUsage keyCode,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,11 @@ - (NSString*)charactersIgnoringModifiers API_AVAILABLE(ios(13.4)) {

FlutterUIPressProxy* keyUpEvent(UIKeyboardHIDUsage keyCode,
UIKeyModifierFlags modifierFlags,
NSTimeInterval timestamp) API_AVAILABLE(ios(13.4)) {
return keyEventWithPhase(UIPressPhaseEnded, keyCode, modifierFlags, timestamp);
NSTimeInterval timestamp,
const char* characters,
const char* charactersIgnoringModifiers) API_AVAILABLE(ios(13.4)) {
return keyEventWithPhase(UIPressPhaseEnded, keyCode, modifierFlags, timestamp, characters,
charactersIgnoringModifiers);
}

FlutterUIPressProxy* keyEventWithPhase(UIPressPhase phase,
Expand Down
25 changes: 25 additions & 0 deletions shell/platform/darwin/ios/framework/Source/KeyCodeMap.g.mm
Original file line number Diff line number Diff line change
Expand Up @@ -328,5 +328,30 @@
0x00000073, // f24
};

API_AVAILABLE(ios(13.4))
NSDictionary<NSString*, NSNumber*>* specialKeyMapping = [[NSDictionary alloc] initWithDictionary:@{
@"UIKeyInputEscape" : @(0x10000001b),
@"UIKeyInputF1" : @(0x100000801),
@"UIKeyInputF2" : @(0x100000802),
@"UIKeyInputF3" : @(0x100000803),
@"UIKeyInputF4" : @(0x100000804),
@"UIKeyInputF5" : @(0x100000805),
@"UIKeyInputF6" : @(0x100000806),
@"UIKeyInputF7" : @(0x100000807),
@"UIKeyInputF8" : @(0x100000808),
@"UIKeyInputF9" : @(0x100000809),
@"UIKeyInputF10" : @(0x10000080a),
@"UIKeyInputF11" : @(0x10000080b),
@"UIKeyInputF12" : @(0x10000080c),
@"UIKeyInputUpArrow" : @(0x100000304),
@"UIKeyInputDownArrow" : @(0x100000301),
@"UIKeyInputLeftArrow" : @(0x100000302),
@"UIKeyInputRightArrow" : @(0x100000303),
@"UIKeyInputHome" : @(0x100000306),
@"UIKeyInputEnd" : @(0x10000000d),
@"UIKeyInputPageUp" : @(0x100000308),
@"UIKeyInputPageDown" : @(0x100000307),
}];

const uint64_t kCapsLockPhysicalKey = 0x00070039;
const uint64_t kCapsLockLogicalKey = 0x100000104;
23 changes: 17 additions & 6 deletions shell/platform/darwin/ios/framework/Source/KeyCodeMap_Internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,18 @@ extern const std::map<uint32_t, uint64_t> keyCodeToPhysicalKey;
*/
extern const std::map<uint32_t, uint64_t> keyCodeToLogicalKey;

/**
* Maps iOS specific string values of nonvisible keys to logical keys.
*
* TODO(dkwingsmt): Change this getter function to a global variable. I tried to
* do this but the unit test on CI threw errors saying "message sent to
* deallocated instance" on the NSDictionary.
*
* See:
* https://developer.apple.com/documentation/uikit/uikeycommand/input_strings_for_special_keys?language=objc
*/
extern NSDictionary<NSString*, NSNumber*>* specialKeyMapping;

// Several mask constants. See KeyCodeMap.g.mm for their descriptions.

extern const uint64_t kValueMask;
Expand Down Expand Up @@ -70,17 +82,16 @@ typedef enum {
* not whether it is the left or right modifier.
*/
constexpr uint32_t kModifierFlagAnyMask =
kModifierFlagShiftAny | kModifierFlagControlAny | kModifierFlagAltAny |
kModifierFlagMetaAny;
kModifierFlagShiftAny | kModifierFlagControlAny | kModifierFlagAltAny | kModifierFlagMetaAny;

/**
* A mask of the modifier flags that represent only left or right modifier
* keys, and not the generic "Any" mask.
*/
constexpr uint32_t kModifierFlagSidedMask =
kModifierFlagControlLeft | kModifierFlagShiftLeft |
kModifierFlagShiftRight | kModifierFlagMetaLeft | kModifierFlagMetaRight |
kModifierFlagAltLeft | kModifierFlagAltRight | kModifierFlagControlRight;
constexpr uint32_t kModifierFlagSidedMask = kModifierFlagControlLeft | kModifierFlagShiftLeft |
kModifierFlagShiftRight | kModifierFlagMetaLeft |
kModifierFlagMetaRight | kModifierFlagAltLeft |
kModifierFlagAltRight | kModifierFlagControlRight;

/**
* Map |UIKey.keyCode| to the matching sided modifier in UIEventModifierFlags.
Expand Down