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 
8 #import "KeyCodeMap_Internal.h"
11 #import "flutter/shell/platform/embedder/embedder.h"
12 
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  };
63  NSEventModifierFlagShift);
65  NSEventModifierFlagControl);
67  NSEventModifierFlagOption);
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
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