Flutter Engine
The Flutter Engine
Instance Methods | Properties | List of all members
FlutterChannelKeyResponder Class Reference

#import <FlutterChannelKeyResponder.h>

Inheritance diagram for FlutterChannelKeyResponder:
<FlutterKeyPrimaryResponder> <FlutterKeyPrimaryResponder>

Instance Methods

(nonnull instancetype) - initWithChannel:
 
(nonnull instancetype) - initWithChannel:
 
(void) - handlePress:callback: [implementation]
 
(void) - updatePressedModifiers: [implementation]
 
(NSInteger) - adjustModifiers: [implementation]
 
(void) - checkModifierFlag:forEventFlags:keyCode:timestamp: [implementation]
 
(void) - syncModifiersIfNeeded:timestamp: [implementation]
 
(void) - handleEvent:callback: [implementation]
 
- Instance Methods inherited from <FlutterKeyPrimaryResponder>
(void) - handlePress:callback:
 
(void) - handleEvent:callback:
 
(void) - syncModifiersIfNeeded:timestamp:
 

Properties

FlutterBasicMessageChannelchannel [implementation]
 
kKeyboardModifier pressedModifiers [implementation]
 
uint64_t previouslyPressedFlags [implementation]
 
- Properties inherited from <FlutterKeyPrimaryResponder>
NSMutableDictionary< NSNumber *, NSNumber * > * layoutMap
 

Detailed Description

A primary responder of |FlutterKeyboardManager| that handles events by sending the raw information through a method channel.

This class corresponds to the RawKeyboard API in the framework.

A primary responder of |FlutterKeyboardManager| that handles events by sending the raw information through the method channel.

This class communicates with the RawKeyboard API in the framework.

Definition at line 20 of file FlutterChannelKeyResponder.h.

Method Documentation

◆ adjustModifiers:

- (NSInteger) adjustModifiers: (ios(13.4))  API_AVAILABLE
implementation

Definition at line 89 of file FlutterChannelKeyResponder.mm.

236 :(nonnull FlutterUIPressProxy*)press API_AVAILABLE(ios(13.4)) {
237 if (@available(iOS 13.4, *)) {
238 // no-op
239 } else {
240 return press.key.modifierFlags;
241 }
242
243 [self updatePressedModifiers:press];
244 // Replace the supplied modifier flags with our computed ones.
245 return _pressedModifiers | (press.key.modifierFlags & ~kKeyboardModifierMask);
246}
SK_API sk_sp< SkSurface > ios(9.0)
API_AVAILABLE(ios(14.0), macos(11.0)) static NSString *MTLCommandEncoderErrorStateToString(MTLCommandEncoderErrorState state)

◆ checkModifierFlag:forEventFlags:keyCode:timestamp:

- (void) checkModifierFlag: (NSUInteger)  targetMask
forEventFlags: (NSEventModifierFlags)  eventFlags
keyCode: (NSUInteger)  keyCode
timestamp: (NSTimeInterval)  timestamp 
implementation

Checks single modifier flag from event flags and sends appropriate key event if it is different from the previous state.

Definition at line 26 of file FlutterChannelKeyResponder.mm.

45 :(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 };
62 NSEventModifierFlagShift);
64 NSEventModifierFlagControl);
66 NSEventModifierFlagOption);
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}
FlutterSemanticsFlag flag
@ kModifierFlagControlLeft
@ kModifierFlagControlRight

◆ handleEvent:callback:

- (void) handleEvent: (NSEvent*)  event
callback: (FlutterAsyncKeyCallback callback 
implementation

Definition at line 26 of file FlutterChannelKeyResponder.mm.

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}
GLenum type
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
FlKeyEvent * event
void(^ FlutterAsyncKeyCallback)(BOOL handled)
NSMutableDictionary< NSNumber *, NSNumber * > * layoutMap

◆ handlePress:callback:

- (void) handlePress: (nonnull FlutterUIPressProxy *)  press
callback: (ios(13.4))  API_AVAILABLE 
implementation

Process the event.

The |callback| should be called with a value that indicates whether the responder has handled the given press event. The |callback| must be called exactly once, and can be called before the return of this method, or after.

Reimplemented from <FlutterKeyPrimaryResponder>.

Definition at line 89 of file FlutterChannelKeyResponder.mm.

103 :(nonnull FlutterUIPressProxy*)press
105 if (@available(iOS 13.4, *)) {
106 // no-op
107 } else {
108 return;
109 }
110 NSString* type;
111 switch (press.phase) {
112 case UIPressPhaseBegan:
113 type = @"keydown";
114 break;
115 case UIPressPhaseCancelled:
116 // This event doesn't appear to happen on iOS, at least when switching
117 // apps. Maybe on tvOS? In any case, it's probably best to send a keyup if
118 // we do receive one, since if the event was canceled, it's likely that a
119 // keyup will never be received otherwise.
120 case UIPressPhaseEnded:
121 type = @"keyup";
122 break;
123 case UIPressPhaseChanged:
124 // This only happens for analog devices like joysticks.
125 return;
126 case UIPressPhaseStationary:
127 // The entire volume of documentation of this phase on the Apple site, and
128 // indeed the Internet, is:
129 // "A button was pressed but hasn’t moved since the previous event."
130 // It's unclear what this is actually used for, and we've yet to see it in
131 // the wild.
132 return;
133 }
134
135 NSString* characters = getEventCharacters(press.key.characters, press.key.keyCode);
136 NSString* charactersIgnoringModifiers =
137 getEventCharacters(press.key.charactersIgnoringModifiers, press.key.keyCode);
138 NSDictionary* keyMessage = @{
139 @"keymap" : @"ios",
140 @"type" : type,
141 @"keyCode" : @(press.key.keyCode),
142 @"modifiers" : @([self adjustModifiers:press]),
143 @"characters" : characters == nil ? @"" : characters,
144 @"charactersIgnoringModifiers" : charactersIgnoringModifiers == nil
145 ? @""
146 : charactersIgnoringModifiers,
147 };
148 [self.channel sendMessage:keyMessage
149 reply:^(id reply) {
150 bool handled = reply ? [[reply valueForKey:@"handled"] boolValue] : true;
151 callback(handled);
152 }];
153}
static NSString * getEventCharacters(NSString *characters, UIKeyboardHIDUsage keyCode) API_AVAILABLE(ios(13.4))

◆ initWithChannel: [1/2]

- (nonnull instancetype) initWithChannel: (nonnull FlutterBasicMessageChannel*)  channel

Create an instance by specifying the method channel to use.

Definition at line 89 of file FlutterChannelKeyResponder.mm.

95 self = [super init];
96 if (self != nil) {
98 _pressedModifiers = 0;
99 }
100 return self;
101}
FlutterMethodChannel * _channel
FlutterBasicMessageChannel * channel

◆ initWithChannel: [2/2]

- (nonnull instancetype) initWithChannel: (nonnull FlutterBasicMessageChannel *)  channel

Create an instance by specifying the method channel to use.

◆ syncModifiersIfNeeded:timestamp:

- (void) syncModifiersIfNeeded: (NSEventModifierFlags)  modifierFlags
timestamp: (NSTimeInterval)  timestamp 
implementation

Synchronize the modifier flags if necessary. The new modifier flag would usually come from mouse event and may be out of sync with current keyboard state if the modifier flags have changed while window was not key.

Reimplemented from <FlutterKeyPrimaryResponder>.

Definition at line 26 of file FlutterChannelKeyResponder.mm.

86 :(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}
#define FML_DCHECK(condition)
Definition: logging.h:103

◆ updatePressedModifiers:

- (void) updatePressedModifiers: (ios(13.4))  API_AVAILABLE
implementation

Definition at line 89 of file FlutterChannelKeyResponder.mm.

157 :(nonnull FlutterUIPressProxy*)press API_AVAILABLE(ios(13.4)) {
158 if (@available(iOS 13.4, *)) {
159 // no-op
160 } else {
161 return;
162 }
163
164 bool isKeyDown = false;
165 switch (press.phase) {
166 case UIPressPhaseStationary:
167 case UIPressPhaseChanged:
168 // These kinds of events shouldn't get this far.
169 NSAssert(false, @"Unexpected key event type received in updatePressedModifiers.");
170 return;
171 case UIPressPhaseBegan:
172 isKeyDown = true;
173 break;
174 case UIPressPhaseCancelled:
175 case UIPressPhaseEnded:
176 isKeyDown = false;
177 break;
178 }
179
180 void (^update)(kKeyboardModifier, bool) = ^(kKeyboardModifier mod, bool isOn) {
181 if (isOn) {
182 _pressedModifiers |= mod;
183 } else {
184 _pressedModifiers &= ~mod;
185 }
186 };
187 switch (press.key.keyCode) {
188 case UIKeyboardHIDUsageKeyboardCapsLock:
189 update(kKeyboardModifierAlphaShift, isKeyDown);
190 break;
191 case UIKeyboardHIDUsageKeypadNumLock:
192 update(kKeyboardModifierNumericPad, isKeyDown);
193 break;
194 case UIKeyboardHIDUsageKeyboardLeftShift:
195 update(kKeyboardModifierLeftShift, isKeyDown);
196 break;
197 case UIKeyboardHIDUsageKeyboardRightShift:
198 update(kKeyboardModifierRightShift, isKeyDown);
199 break;
200 case UIKeyboardHIDUsageKeyboardLeftControl:
201 update(kKeyboardModifierLeftControl, isKeyDown);
202 break;
203 case UIKeyboardHIDUsageKeyboardRightControl:
204 update(kKeyboardModifierRightControl, isKeyDown);
205 break;
206 case UIKeyboardHIDUsageKeyboardLeftAlt:
207 update(kKeyboardModifierLeftOption, isKeyDown);
208 break;
209 case UIKeyboardHIDUsageKeyboardRightAlt:
210 update(kKeyboardModifierRightOption, isKeyDown);
211 break;
212 case UIKeyboardHIDUsageKeyboardLeftGUI:
213 update(kKeyboardModifierLeftCommand, isKeyDown);
214 break;
215 case UIKeyboardHIDUsageKeyboardRightGUI:
216 update(kKeyboardModifierRightCommand, isKeyDown);
217 break;
218 default:
219 // If we didn't update any of the modifiers above, we're done.
220 return;
221 }
222 // Update the non-sided modifier flags to match the content of the sided ones.
223 update(kKeyboardModifierShift,
224 _pressedModifiers & (kKeyboardModifierRightShift | kKeyboardModifierLeftShift));
225 update(kKeyboardModifierControl,
226 _pressedModifiers & (kKeyboardModifierRightControl | kKeyboardModifierLeftControl));
227 update(kKeyboardModifierOption,
228 _pressedModifiers & (kKeyboardModifierRightOption | kKeyboardModifierLeftOption));
229 update(kKeyboardModifierCommand,
230 _pressedModifiers & (kKeyboardModifierRightCommand | kKeyboardModifierLeftCommand));
231}
static bool isKeyDown(FlutterUIPressProxy *press) API_AVAILABLE(ios(13.4))
Definition: update.py:1

Property Documentation

◆ channel

- (FlutterBasicMessageChannel*) channel
readwritenonatomicassignimplementation

The channel used to communicate with Flutter.

Definition at line 84 of file FlutterChannelKeyResponder.mm.

◆ pressedModifiers

- (kKeyboardModifier) pressedModifiers
readwritenonatomicassignimplementation

Definition at line 89 of file FlutterChannelKeyResponder.mm.

◆ previouslyPressedFlags

- (uint64_t) previouslyPressedFlags
readwritenonatomicassignimplementation

The |NSEvent.modifierFlags| of the last event received.

Used to determine whether a FlagsChanged event should count as a keydown or a keyup event.

Definition at line 26 of file FlutterChannelKeyResponder.mm.


The documentation for this class was generated from the following files: