| Flutter macOS Embedder |

FlutterChannelKeyResponder.mm
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #import <objc/message.h>
6
7 #import "FlutterChannelKeyResponder.h"
8 #import "KeyCodeMap_Internal.h"
9 #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h"
10 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h"
11 #import "flutter/shell/platform/embedder/embedder.h"
12
13 @interface FlutterChannelKeyResponder ()
14
15 /**
16 * The channel used to communicate with Flutter.
17 */
18 @property(nonatomic) FlutterBasicMessageChannel* channel;
19
20 /**
21 * The |NSEvent.modifierFlags| of the last event received.
22 *
23 * Used to determine whether a FlagsChanged event should count as a keydown or
24 * a keyup event.
25 */
26 @property(nonatomic) uint64_t previouslyPressedFlags;
27
28 @end
29
30 @implementation FlutterChannelKeyResponder
31
32 // Synthesize properties declared in FlutterKeyPrimaryResponder protocol.
33 @synthesize layoutMap;
34
35 - (nonnull instancetype)initWithChannel:(nonnull FlutterBasicMessageChannel*)channel {
36 self = [super init];
37 if (self != nil) {
38 _channel = channel;
39 _previouslyPressedFlags = 0;
40 }
41 return self;
42 }
43
44 /// Checks single modifier flag from event flags and sends appropriate key event
45 /// if it is different from the previous state.
46 - (void)checkModifierFlag:(NSUInteger)targetMask
47 forEventFlags:(NSEventModifierFlags)eventFlags
48 keyCode:(NSUInteger)keyCode
49 timestamp:(NSTimeInterval)timestamp {
50 NSAssert((targetMask & (targetMask - 1)) == 0, @"targetMask must only have one bit set");
51 if ((eventFlags & targetMask) != (_previouslyPressedFlags & targetMask)) {
52 uint64_t newFlags = (_previouslyPressedFlags & ~targetMask) | (eventFlags & targetMask);
53
54 // Sets combined flag if either left or right modifier is pressed, unsets otherwise.
55 auto updateCombinedFlag = [&](uint64_t side1, uint64_t side2, NSEventModifierFlags flag) {
56 if (newFlags & (side1 | side2)) {
57 newFlags |= flag;
58 } else {
59 newFlags &= ~flag;
60 }
61 };
62 updateCombinedFlag(flutter::kModifierFlagShiftLeft, flutter::kModifierFlagShiftRight,
63 NSEventModifierFlagShift);
64 updateCombinedFlag(flutter::kModifierFlagControlLeft, flutter::kModifierFlagControlRight,
65 NSEventModifierFlagControl);
66 updateCombinedFlag(flutter::kModifierFlagAltLeft, flutter::kModifierFlagAltRight,
67 NSEventModifierFlagOption);
68 updateCombinedFlag(flutter::kModifierFlagMetaLeft, flutter::kModifierFlagMetaRight,
69 NSEventModifierFlagCommand);
70
71 NSEvent* event = [NSEvent keyEventWithType:NSEventTypeFlagsChanged
72 location:NSZeroPoint
73 modifierFlags:newFlags
74 timestamp:timestamp
75 windowNumber:0
76 context:nil
77 characters:@""
78 charactersIgnoringModifiers:@""
79 isARepeat:NO
80 keyCode:keyCode];
81 [self handleEvent:event
82 callback:^(BOOL){
83 }];
84 };
85 }
86
87 - (void)syncModifiersIfNeeded:(NSEventModifierFlags)modifierFlags
88 timestamp:(NSTimeInterval)timestamp {
89 modifierFlags = modifierFlags & ~0x100;
90 if (_previouslyPressedFlags == modifierFlags) {
91 return;
92 }
93
94 [flutter::modifierFlagToKeyCode
95 enumerateKeysAndObjectsUsingBlock:^(NSNumber* flag, NSNumber* keyCode, BOOL* stop) {
96 [self checkModifierFlag:[flag unsignedShortValue]
97 forEventFlags:modifierFlags
98 keyCode:[keyCode unsignedShortValue]
99 timestamp:timestamp];
100 }];
101
102 // Caps lock is not included in the modifierFlagToKeyCode map.
103 [self checkModifierFlag:NSEventModifierFlagCapsLock
104 forEventFlags:modifierFlags
105 keyCode:0x00000039 // kVK_CapsLock
106 timestamp:timestamp];
107 }
108
109 - (void)handleEvent:(NSEvent*)event callback:(FlutterAsyncKeyCallback)callback {
110 // Remove the modifier bits that Flutter is not interested in.
111 NSEventModifierFlags modifierFlags = event.modifierFlags & ~0x100;
112 NSString* type;
113 switch (event.type) {
114 case NSEventTypeKeyDown:
115 type = @"keydown";
116 break;
117 case NSEventTypeKeyUp:
118 type = @"keyup";
119 break;
120 case NSEventTypeFlagsChanged:
121 if (modifierFlags < _previouslyPressedFlags) {
122 type = @"keyup";
123 } else if (modifierFlags > _previouslyPressedFlags) {
124 type = @"keydown";
125 } else {
126 // ignore duplicate modifiers; This can happen in situations like switching
127 // between application windows when MacOS only sends the up event to new window.
128 callback(true);
129 return;
130 }
131 break;
132 default:
133 [[unlikely]] {
134 NSAssert(false, @"Unexpected key event type (got %lu).", event.type);
135 callback(false);
136 // This should not happen. Return to suppress clang-tidy warning on `type` being nil.
137 return;
138 }
139 }
140 _previouslyPressedFlags = modifierFlags;
141 NSMutableDictionary* keyMessage = [@{
142 @"keymap" : @"macos",
143 @"type" : type,
144 @"keyCode" : @(event.keyCode),
145 @"modifiers" : @(modifierFlags),
146 } mutableCopy];
147 // Calling these methods on any other type of event
148 // (e.g NSEventTypeFlagsChanged) will raise an exception.
149 if (event.type == NSEventTypeKeyDown || event.type == NSEventTypeKeyUp) {
150 keyMessage[@"characters"] = event.characters;
151 keyMessage[@"charactersIgnoringModifiers"] = event.charactersIgnoringModifiers;
152 }
153 NSNumber* specifiedLogicalKey = layoutMap[@(event.keyCode)];
154 if (specifiedLogicalKey != nil) {
155 keyMessage[@"specifiedLogicalKey"] = specifiedLogicalKey;
156 }
157 [self.channel sendMessage:keyMessage
158 reply:^(id reply) {
159 if (!reply) {
160 return callback(true);
161 }
162 // Only propagate the event to other responders if the framework didn't
163 // handle it.
164 callback([[reply valueForKey:@"handled"] boolValue]);
165 }];
166 }
167
168 #pragma mark - Private
169
170 @end
FlutterChannelKeyResponder.h
FlutterCodecs.h
FlutterAsyncKeyCallback
void(^ FlutterAsyncKeyCallback)(BOOL handled)
Definition: FlutterKeyPrimaryResponder.h:10
FlutterViewController_Internal.h
KeyCodeMap_Internal.h
FlutterBasicMessageChannel
Definition: FlutterChannels.h:38
FlutterChannelKeyResponder
Definition: FlutterChannelKeyResponder.h:20
flutter::kModifierFlagShiftLeft
@ kModifierFlagShiftLeft
Definition: KeyCodeMap_Internal.h:81
flutter::kModifierFlagAltLeft
@ kModifierFlagAltLeft
Definition: KeyCodeMap_Internal.h:85
flutter::kModifierFlagMetaRight
@ kModifierFlagMetaRight
Definition: KeyCodeMap_Internal.h:84
flutter::kModifierFlagMetaLeft
@ kModifierFlagMetaLeft
Definition: KeyCodeMap_Internal.h:83
flutter::kModifierFlagControlLeft
@ kModifierFlagControlLeft
Definition: KeyCodeMap_Internal.h:80
flutter::kModifierFlagAltRight
@ kModifierFlagAltRight
Definition: KeyCodeMap_Internal.h:86
flutter::kModifierFlagControlRight
@ kModifierFlagControlRight
Definition: KeyCodeMap_Internal.h:87
flutter::kModifierFlagShiftRight
@ kModifierFlagShiftRight
Definition: KeyCodeMap_Internal.h:82
FlutterKeyPrimaryResponder-p::layoutMap
NSMutableDictionary< NSNumber *, NSNumber * > * layoutMap
Definition: FlutterKeyPrimaryResponder.h:46