| 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 layoutMap;
33
34 - (nonnull instancetype)initWithChannel:(nonnull FlutterBasicMessageChannel*)channel {
35 self = [super init];
36 if (self != nil) {
37 _channel = channel;
38 _previouslyPressedFlags = 0;
39 }
40 return self;
41 }
42
43 /// Checks single modifier flag from event flags and sends appropriate key event
44 /// if it is different from the previous state.
45 - (void)checkModifierFlag:(NSUInteger)targetMask
46 forEventFlags:(NSEventModifierFlags)eventFlags
47 keyCode:(NSUInteger)keyCode
48 timestamp:(NSTimeInterval)timestamp {
49 NSAssert((targetMask & (targetMask - 1)) == 0, @"targetMask must only have one bit set");
50 if ((eventFlags & targetMask) != (_previouslyPressedFlags & targetMask)) {
51 uint64_t newFlags = (_previouslyPressedFlags & ~targetMask) | (eventFlags & targetMask);
52
53 // Sets combined flag if either left or right modifier is pressed, unsets otherwise.
54 auto updateCombinedFlag = [&](uint64_t side1, uint64_t side2, NSEventModifierFlags flag) {
55 if (newFlags & (side1 | side2)) {
56 newFlags |= flag;
57 } else {
58 newFlags &= ~flag;
59 }
60 };
61 updateCombinedFlag(flutter::kModifierFlagShiftLeft, flutter::kModifierFlagShiftRight,
62 NSEventModifierFlagShift);
63 updateCombinedFlag(flutter::kModifierFlagControlLeft, flutter::kModifierFlagControlRight,
64 NSEventModifierFlagControl);
65 updateCombinedFlag(flutter::kModifierFlagAltLeft, flutter::kModifierFlagAltRight,
66 NSEventModifierFlagOption);
67 updateCombinedFlag(flutter::kModifierFlagMetaLeft, flutter::kModifierFlagMetaRight,
68 NSEventModifierFlagCommand);
69
70 NSEvent* event = [NSEvent keyEventWithType:NSEventTypeFlagsChanged
71 location:NSZeroPoint
72 modifierFlags:newFlags
73 timestamp:timestamp
74 windowNumber:0
75 context:nil
76 characters:@""
77 charactersIgnoringModifiers:@""
78 isARepeat:NO
79 keyCode:keyCode];
80 [self handleEvent:event
81 callback:^(BOOL){
82 }];
83 };
84 }
85
86 - (void)syncModifiersIfNeeded:(NSEventModifierFlags)modifierFlags
87 timestamp:(NSTimeInterval)timestamp {
88 modifierFlags = modifierFlags & ~0x100;
89 if (_previouslyPressedFlags == modifierFlags) {
90 return;
91 }
92
93 [flutter::modifierFlagToKeyCode
94 enumerateKeysAndObjectsUsingBlock:^(NSNumber* flag, NSNumber* keyCode, BOOL* stop) {
95 [self checkModifierFlag:[flag unsignedShortValue]
96 forEventFlags:modifierFlags
97 keyCode:[keyCode unsignedShortValue]
98 timestamp:timestamp];
99 }];
100
101 // Caps lock is not included in the modifierFlagToKeyCode map.
102 [self checkModifierFlag:NSEventModifierFlagCapsLock
103 forEventFlags:modifierFlags
104 keyCode:0x00000039 // kVK_CapsLock
105 timestamp:timestamp];
106
107 // At the end we should end up with the same modifier flags as the event.
108 FML_DCHECK(_previouslyPressedFlags == modifierFlags);
109 }
110
111 - (void)handleEvent:(NSEvent*)event callback:(FlutterAsyncKeyCallback)callback {
112 // Remove the modifier bits that Flutter is not interested in.
113 NSEventModifierFlags modifierFlags = event.modifierFlags & ~0x100;
114 NSString* type;
115 switch (event.type) {
116 case NSEventTypeKeyDown:
117 type = @"keydown";
118 break;
119 case NSEventTypeKeyUp:
120 type = @"keyup";
121 break;
122 case NSEventTypeFlagsChanged:
123 if (modifierFlags < _previouslyPressedFlags) {
124 type = @"keyup";
125 } else if (modifierFlags > _previouslyPressedFlags) {
126 type = @"keydown";
127 } else {
128 // ignore duplicate modifiers; This can happen in situations like switching
129 // between application windows when MacOS only sends the up event to new window.
130 callback(true);
131 return;
132 }
133 break;
134 default: {
135 NSAssert(false, @"Unexpected key event type (got %lu).", event.type);
136 callback(false);
137 }
138 }
139 _previouslyPressedFlags = modifierFlags;
140 NSMutableDictionary* keyMessage = [@{
141 @"keymap" : @"macos",
142 @"type" : type,
143 @"keyCode" : @(event.keyCode),
144 @"modifiers" : @(modifierFlags),
145 } mutableCopy];
146 // Calling these methods on any other type of event
147 // (e.g NSEventTypeFlagsChanged) will raise an exception.
148 if (event.type == NSEventTypeKeyDown || event.type == NSEventTypeKeyUp) {
149 keyMessage[@"characters"] = event.characters;
150 keyMessage[@"charactersIgnoringModifiers"] = event.charactersIgnoringModifiers;
151 }
152 NSNumber* specifiedLogicalKey = layoutMap[@(event.keyCode)];
153 if (specifiedLogicalKey != nil) {
154 keyMessage[@"specifiedLogicalKey"] = specifiedLogicalKey;
155 }
156 [self.channel sendMessage:keyMessage
157 reply:^(id reply) {
158 if (!reply) {
159 return callback(true);
160 }
161 // Only propagate the event to other responders if the framework didn't
162 // handle it.
163 callback([[reply valueForKey:@"handled"] boolValue]);
164 }];
165 }
166
167 #pragma mark - Private
168
169 @end
FlutterBasicMessageChannel
Definition: FlutterChannels.h:37
flutter::kModifierFlagMetaLeft
@ kModifierFlagMetaLeft
Definition: KeyCodeMap_Internal.h:83
flutter::kModifierFlagAltRight
@ kModifierFlagAltRight
Definition: KeyCodeMap_Internal.h:86
FlutterChannelKeyResponder.h
flutter::kModifierFlagMetaRight
@ kModifierFlagMetaRight
Definition: KeyCodeMap_Internal.h:84
flutter::kModifierFlagControlLeft
@ kModifierFlagControlLeft
Definition: KeyCodeMap_Internal.h:80
flutter::kModifierFlagAltLeft
@ kModifierFlagAltLeft
Definition: KeyCodeMap_Internal.h:85
FlutterAsyncKeyCallback
void(^ FlutterAsyncKeyCallback)(BOOL handled)
Definition: FlutterKeyPrimaryResponder.h:10
FlutterCodecs.h
flutter::kModifierFlagShiftRight
@ kModifierFlagShiftRight
Definition: KeyCodeMap_Internal.h:82
FlutterKeyPrimaryResponder-p::layoutMap
NSMutableDictionary< NSNumber *, NSNumber * > * layoutMap
Definition: FlutterKeyPrimaryResponder.h:46
FlutterChannelKeyResponder
Definition: FlutterChannelKeyResponder.h:20
FlutterViewController_Internal.h
KeyCodeMap_Internal.h
flutter::kModifierFlagShiftLeft
@ kModifierFlagShiftLeft
Definition: KeyCodeMap_Internal.h:81
flutter::kModifierFlagControlRight
@ kModifierFlagControlRight
Definition: KeyCodeMap_Internal.h:87