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 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  };
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 }
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