| Flutter macOS Embedder |

FlutterKeyboardLayout.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 "flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyboardLayout.h"
6
7 #include <Carbon/Carbon.h>
8 #include <cctype>
9 #include "flutter/fml/platform/darwin/cf_utils.h"
10
11 @implementation FlutterKeyboardLayout {
12 NSData* _keyboardLayoutData;
13 }
14
15 @synthesize delegate = _delegate;
16
17 /**
18 * Returns the current Unicode layout data (kTISPropertyUnicodeKeyLayoutData).
19 *
20 * To use the returned data, convert it to CFDataRef first, finds its bytes
21 * with CFDataGetBytePtr, then reinterpret it into const UCKeyboardLayout*.
22 * It's returned in NSData* to enable auto reference count.
23 */
24 static NSData* CurrentKeyboardLayoutData() {
25 fml::CFRef<TISInputSourceRef> source(TISCopyCurrentKeyboardInputSource());
26 CFTypeRef layout_data = TISGetInputSourceProperty(source, kTISPropertyUnicodeKeyLayoutData);
27 if (layout_data == nil) {
28 // TISGetInputSourceProperty returns null with Japanese keyboard layout.
29 // Using TISCopyCurrentKeyboardLayoutInputSource to fix NULL return.
30 // https://github.com/microsoft/node-native-keymap/blob/5f0699ded00179410a14c0e1b0e089fe4df8e130/src/keyboard_mac.mm#L91
31 source.Reset(TISCopyCurrentKeyboardLayoutInputSource());
32 layout_data = TISGetInputSourceProperty(source, kTISPropertyUnicodeKeyLayoutData);
33 }
34 return (__bridge NSData*)layout_data;
35 }
36
37 /**
38 * NotificationCenter callback invoked on kTISNotifySelectedKeyboardInputSourceChanged events.
39 */
40 static void OnKeyboardLayoutChanged(CFNotificationCenterRef center,
41 void* observer,
42 CFStringRef name,
43 const void* object,
44 CFDictionaryRef userInfo) {
45 FlutterKeyboardLayout* controller = (__bridge FlutterKeyboardLayout*)observer;
46 if (controller != nil) {
47 [controller onKeyboardLayoutChanged];
48 }
49 }
50
51 - (instancetype)initWithDelegate:(id<FlutterKeyboardLayoutDelegate>)delegate {
52 self = [super init];
53 if (self) {
54 _delegate = delegate;
55 // macOS fires this message when changing IMEs.
56 CFNotificationCenterRef cfCenter = CFNotificationCenterGetDistributedCenter();
57 __weak FlutterKeyboardLayout* weakSelf = self;
58 CFNotificationCenterAddObserver(cfCenter, (__bridge void*)weakSelf, OnKeyboardLayoutChanged,
59 kTISNotifySelectedKeyboardInputSourceChanged, NULL,
60 CFNotificationSuspensionBehaviorDeliverImmediately);
61 }
62 return self;
63 }
64
65 - (void)dealloc {
66 CFNotificationCenterRef cfCenter = CFNotificationCenterGetDistributedCenter();
67 CFNotificationCenterRemoveEveryObserver(cfCenter, (__bridge void*)self);
68 }
69
70 - (void)onKeyboardLayoutChanged {
71 _keyboardLayoutData = nil;
72 [_delegate keyboardLayoutDidChange];
73 }
74
75 - (flutter::LayoutClue)lookUpLayoutForKeyCode:(uint16_t)keyCode shift:(BOOL)shift {
76 if (_keyboardLayoutData == nil) {
77 _keyboardLayoutData = CurrentKeyboardLayoutData();
78 }
79 const UCKeyboardLayout* layout = reinterpret_cast<const UCKeyboardLayout*>(
80 CFDataGetBytePtr((__bridge CFDataRef)_keyboardLayoutData));
81
82 UInt32 deadKeyState = 0;
83 UniCharCount stringLength = 0;
84 UniChar resultChar;
85
86 UInt32 modifierState = ((shift ? shiftKey : 0) >> 8) & 0xFF;
87 UInt32 keyboardType = LMGetKbdLast();
88
89 bool isDeadKey = false;
90 OSStatus status =
91 UCKeyTranslate(layout, keyCode, kUCKeyActionDown, modifierState, keyboardType,
92 kUCKeyTranslateNoDeadKeysBit, &deadKeyState, 1, &stringLength, &resultChar);
93 // For dead keys, press the same key again to get the printable representation of the key.
94 if (status == noErr && stringLength == 0 && deadKeyState != 0) {
95 isDeadKey = true;
96 status =
97 UCKeyTranslate(layout, keyCode, kUCKeyActionDown, modifierState, keyboardType,
98 kUCKeyTranslateNoDeadKeysBit, &deadKeyState, 1, &stringLength, &resultChar);
99 }
100
101 if (status == noErr && stringLength == 1 && !std::iscntrl(resultChar)) {
102 return flutter::LayoutClue{resultChar, isDeadKey};
103 }
104 return flutter::LayoutClue{0, false};
105 }
106
107 @end
FlutterKeyboardLayout.h
FlutterKeyboardLayout
Definition: FlutterKeyboardLayout.h:51
flutter
Definition: AccessibilityBridgeMac.h:16
FlutterKeyboardLayoutDelegate-p
Definition: FlutterKeyboardLayout.h:33
flutter::LayoutClue
Definition: FlutterKeyboardLayout.h:14