blob: bcb7aa370ea767405973ad9fab195899f384c8ff [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import <objc/message.h>
#import "FlutterChannelKeyResponder.h"
#import "KeyCodeMap_Internal.h"
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h"
#import "flutter/shell/platform/embedder/embedder.h"
@interface FlutterChannelKeyResponder ()
/**
* The channel used to communicate with Flutter.
*/
@property(nonatomic) FlutterBasicMessageChannel* channel;
/**
* The |NSEvent.modifierFlags| of the last event received.
*
* Used to determine whether a FlagsChanged event should count as a keydown or
* a keyup event.
*/
@property(nonatomic) uint64_t previouslyPressedFlags;
@end
@implementation FlutterChannelKeyResponder
@synthesize layoutMap;
- (nonnull instancetype)initWithChannel:(nonnull FlutterBasicMessageChannel*)channel {
self = [super init];
if (self != nil) {
_channel = channel;
_previouslyPressedFlags = 0;
}
return self;
}
- (void)handleEvent:(NSEvent*)event callback:(FlutterAsyncKeyCallback)callback {
// Remove the modifier bits that Flutter is not interested in.
NSEventModifierFlags modifierFlags = event.modifierFlags & ~0x100;
NSString* type;
switch (event.type) {
case NSEventTypeKeyDown:
type = @"keydown";
break;
case NSEventTypeKeyUp:
type = @"keyup";
break;
case NSEventTypeFlagsChanged:
if (modifierFlags < _previouslyPressedFlags) {
type = @"keyup";
} else if (modifierFlags > _previouslyPressedFlags) {
type = @"keydown";
} else {
// ignore duplicate modifiers; This can happen in situations like switching
// between application windows when MacOS only sends the up event to new window.
callback(true);
return;
}
break;
default: {
NSAssert(false, @"Unexpected key event type (got %lu).", event.type);
callback(false);
}
}
_previouslyPressedFlags = modifierFlags;
NSMutableDictionary* keyMessage = [@{
@"keymap" : @"macos",
@"type" : type,
@"keyCode" : @(event.keyCode),
@"modifiers" : @(modifierFlags),
} mutableCopy];
// Calling these methods on any other type of event
// (e.g NSEventTypeFlagsChanged) will raise an exception.
if (event.type == NSEventTypeKeyDown || event.type == NSEventTypeKeyUp) {
keyMessage[@"characters"] = event.characters;
keyMessage[@"charactersIgnoringModifiers"] = event.charactersIgnoringModifiers;
}
NSNumber* specifiedLogicalKey = layoutMap[@(event.keyCode)];
if (specifiedLogicalKey != nil) {
keyMessage[@"specifiedLogicalKey"] = specifiedLogicalKey;
}
[self.channel sendMessage:keyMessage
reply:^(id reply) {
if (!reply) {
return callback(true);
}
// Only propagate the event to other responders if the framework didn't
// handle it.
callback([[reply valueForKey:@"handled"] boolValue]);
}];
}
#pragma mark - Private
@end