Skip to content

Commit c46a591

Browse files
committed
Add KeyCodeConverter for conversion of chars to key codes
1 parent e93d463 commit c46a591

File tree

9 files changed

+177
-70
lines changed

9 files changed

+177
-70
lines changed

CodingKeys.xcodeproj/project.pbxproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10+
DC2DB8971831897800959260 /* KeyCodeConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = DC2DB8961831897800959260 /* KeyCodeConverter.m */; };
1011
DCC6EB68182D9E3700E2EAE6 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCC6EB67182D9E3700E2EAE6 /* Cocoa.framework */; };
1112
DCC6EB72182D9E3700E2EAE6 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DCC6EB70182D9E3700E2EAE6 /* InfoPlist.strings */; };
1213
DCC6EB74182D9E3700E2EAE6 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC6EB73182D9E3700E2EAE6 /* main.m */; };
@@ -36,6 +37,8 @@
3637
/* End PBXContainerItemProxy section */
3738

3839
/* Begin PBXFileReference section */
40+
DC2DB8951831897800959260 /* KeyCodeConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KeyCodeConverter.h; sourceTree = "<group>"; };
41+
DC2DB8961831897800959260 /* KeyCodeConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KeyCodeConverter.m; sourceTree = "<group>"; };
3942
DCC6EB64182D9E3700E2EAE6 /* CodingKeys.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CodingKeys.app; sourceTree = BUILT_PRODUCTS_DIR; };
4043
DCC6EB67182D9E3700E2EAE6 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
4144
DCC6EB6A182D9E3700E2EAE6 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
@@ -138,6 +141,8 @@
138141
DCC6EB9E182E8E5D00E2EAE6 /* HotKeyService.m */,
139142
DCC6EBAC182FEE5E00E2EAE6 /* HotKey.h */,
140143
DCC6EBAD182FEE5E00E2EAE6 /* HotKey.m */,
144+
DC2DB8951831897800959260 /* KeyCodeConverter.h */,
145+
DC2DB8961831897800959260 /* KeyCodeConverter.m */,
141146
DCC6EB7C182D9E3700E2EAE6 /* MainMenu.xib */,
142147
DCC6EB7F182D9E3700E2EAE6 /* Images.xcassets */,
143148
DCC6EBA6182E955A00E2EAE6 /* data */,
@@ -287,6 +292,7 @@
287292
DCC6EBAB182E968800E2EAE6 /* AppService.m in Sources */,
288293
DCC6EB7B182D9E3700E2EAE6 /* AppDelegate.m in Sources */,
289294
DCC6EB74182D9E3700E2EAE6 /* main.m in Sources */,
295+
DC2DB8971831897800959260 /* KeyCodeConverter.m in Sources */,
290296
);
291297
runOnlyForDeploymentPostprocessing = 0;
292298
};

CodingKeys/AppDelegate.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#import "AppService.h"
33
#import "HotKeyService.h"
44
#import "HotKey.h"
5+
#import <Carbon/Carbon.h>
56

67
@interface AppDelegate ()
78

CodingKeys/HotKey.m

Lines changed: 9 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#import "HotKey.h"
2+
#import "KeyCodeConverter.h"
23
#import <Carbon/Carbon.h>
34

45
@interface HotKey ()
@@ -13,65 +14,6 @@ @interface HotKey ()
1314

1415
@implementation HotKey
1516

16-
// Mapping from https://github.com/davedelong/DDHotKey/
17-
18-
+ (NSDictionary *)keyMapping {
19-
static NSDictionary *keyCodeMap = nil;
20-
static dispatch_once_t onceToken;
21-
dispatch_once(&onceToken, ^{
22-
keyCodeMap = @{
23-
@"":@(kVK_Return),
24-
@"":@(kVK_Tab),
25-
@"":@(kVK_Space),
26-
@"":@(kVK_Delete),
27-
@"":@(kVK_Escape),
28-
@"":@(kVK_Command),
29-
@"":@(kVK_Shift),
30-
@"":@(kVK_CapsLock),
31-
@"":@(kVK_Option),
32-
@"":@(kVK_Control),
33-
@"":@(kVK_RightShift),
34-
@"":@(kVK_RightOption),
35-
@"":@(kVK_RightControl),
36-
@"🔊":@(kVK_VolumeUp),
37-
@"🔈":@(kVK_VolumeDown),
38-
@"🔇":@(kVK_Mute),
39-
@"\u2318":@(kVK_Function),
40-
@"F1":@(kVK_F1),
41-
@"F2":@(kVK_F2),
42-
@"F3":@(kVK_F3),
43-
@"F4":@(kVK_F4),
44-
@"F5":@(kVK_F5),
45-
@"F6":@(kVK_F6),
46-
@"F7":@(kVK_F7),
47-
@"F8":@(kVK_F8),
48-
@"F9":@(kVK_F9),
49-
@"F10":@(kVK_F10),
50-
@"F11":@(kVK_F11),
51-
@"F12":@(kVK_F12),
52-
@"F13":@(kVK_F13),
53-
@"F14":@(kVK_F14),
54-
@"F15":@(kVK_F15),
55-
@"F16":@(kVK_F16),
56-
@"F17":@(kVK_F17),
57-
@"F18":@(kVK_F18),
58-
@"F19":@(kVK_F19),
59-
@"F20":@(kVK_F20),
60-
@"":@(kVK_ForwardDelete),
61-
@"":@(kVK_Home),
62-
@"":@(kVK_End),
63-
@"":@(kVK_PageUp),
64-
@"":@(kVK_PageDown),
65-
@"":@(kVK_LeftArrow),
66-
@"":@(kVK_RightArrow),
67-
@"":@(kVK_DownArrow),
68-
@"":@(kVK_UpArrow)
69-
};
70-
});
71-
72-
return keyCodeMap;
73-
}
74-
7517
- (id)initWithKey:(NSString *)key mapping:(NSDictionary *)mapping {
7618
self = [super init];
7719
if (self) {
@@ -87,16 +29,16 @@ - (void)setup {
8729
}
8830

8931
- (void)parseKey {
90-
NSDictionary *keyMapping = [[self class] keyMapping];
32+
NSDictionary *fixKeys = [KeyCodeConverter fixKeys];
9133

9234
NSArray *components = [self.key componentsSeparatedByString:@" "];
9335

9436
int modifiers = 0;
9537
int carbonModifiers = 0;
9638

9739
for (NSString *component in components) {
98-
int keyCode = [keyMapping[component] intValue];
99-
40+
int keyCode = [fixKeys[component] intValue];
41+
10042
switch (keyCode) {
10143
case kVK_Shift:
10244
modifiers |= kCGEventFlagMaskShift;
@@ -115,7 +57,11 @@ - (void)parseKey {
11557
carbonModifiers |= cmdKey;
11658
break;
11759
default:
118-
self.keyCode = keyCode;
60+
if (fixKeys[component]) {
61+
self.keyCode = keyCode;
62+
} else {
63+
self.keyCode = [KeyCodeConverter toKeyCode:component];
64+
}
11965
break;
12066
}
12167
}

CodingKeys/HotKeyService.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ extern NSString * const HotKeyHandlerDidTriggerHotKey;
1010

1111
- (HotKey *)registerHotKey:(HotKey *)hotKey;
1212
- (void)unregisterAllHotKeys;
13-
1413
- (void)dispatchKeyEventForHotKey:(HotKey *)hotKey;
1514

1615
@end

CodingKeys/HotKeyService.m

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,6 @@ static OSStatus hotKeyHandler(EventHandlerCallRef nextHandler,
8585
EventRef theEvent,
8686
void *userData) {
8787
@autoreleasepool {
88-
8988
EventHotKeyID hotKeyID;
9089
GetEventParameter(theEvent,
9190
kEventParamDirectObject,
@@ -99,7 +98,6 @@ static OSStatus hotKeyHandler(EventHandlerCallRef nextHandler,
9998

10099
HotKey *hotKey = [this findHotKeyByID:keyID];
101100
[this dispatchNotificationForHotKey:hotKey];
102-
103101
}
104102

105103
return noErr;
@@ -114,9 +112,11 @@ - (void)dispatchNotificationForHotKey:(HotKey *)hotKey {
114112
}
115113

116114
- (void)unregisterAllHotKeys {
117-
NSDictionary *hotKeys = [self.hotKeys copy];
118-
for (id keyID in hotKeys) {
119-
[self unregisterHotKey:hotKeys[keyID]];
115+
@autoreleasepool {
116+
NSDictionary *hotKeys = [self.hotKeys copy];
117+
for (id keyID in hotKeys) {
118+
[self unregisterHotKey:hotKeys[keyID]];
119+
}
120120
}
121121
}
122122

CodingKeys/Images.xcassets/status_bar_icon.imageset/Contents.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
"idiom" : "universal",
55
"scale" : "1x",
66
"filename" : "status_bar_icon.png"
7+
},
8+
{
9+
"idiom" : "universal",
10+
"scale" : "2x"
711
}
812
],
913
"info" : {

CodingKeys/KeyCodeConverter.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#import <Foundation/Foundation.h>
2+
3+
@interface KeyCodeConverter : NSObject
4+
5+
+ (NSDictionary *)fixKeys;
6+
7+
+ (int)toKeyCode:(NSString *)character;
8+
9+
@end

CodingKeys/KeyCodeConverter.m

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
#import "KeyCodeConverter.h"
2+
#import <Carbon/Carbon.h>
3+
4+
@implementation KeyCodeConverter
5+
6+
+ (int)toKeyCode:(NSString *)character {
7+
return keyCodeForChar([[character lowercaseString] characterAtIndex:0]);
8+
}
9+
10+
// Mapping from https://github.com/davedelong/DDHotKey/
11+
12+
+ (NSDictionary *)fixKeys {
13+
static NSDictionary *keyCodeMap = nil;
14+
static dispatch_once_t onceToken;
15+
dispatch_once(&onceToken, ^{
16+
keyCodeMap = @{
17+
@"":@(kVK_Return),
18+
@"":@(kVK_Tab),
19+
@"":@(kVK_Space),
20+
@"":@(kVK_Delete),
21+
@"":@(kVK_Escape),
22+
@"":@(kVK_Command),
23+
@"":@(kVK_Shift),
24+
@"":@(kVK_CapsLock),
25+
@"":@(kVK_Option),
26+
@"":@(kVK_Control),
27+
@"":@(kVK_RightShift),
28+
@"":@(kVK_RightOption),
29+
@"":@(kVK_RightControl),
30+
@"🔊":@(kVK_VolumeUp),
31+
@"🔈":@(kVK_VolumeDown),
32+
@"🔇":@(kVK_Mute),
33+
@"\u2318":@(kVK_Function),
34+
@"F1":@(kVK_F1),
35+
@"F2":@(kVK_F2),
36+
@"F3":@(kVK_F3),
37+
@"F4":@(kVK_F4),
38+
@"F5":@(kVK_F5),
39+
@"F6":@(kVK_F6),
40+
@"F7":@(kVK_F7),
41+
@"F8":@(kVK_F8),
42+
@"F9":@(kVK_F9),
43+
@"F10":@(kVK_F10),
44+
@"F11":@(kVK_F11),
45+
@"F12":@(kVK_F12),
46+
@"F13":@(kVK_F13),
47+
@"F14":@(kVK_F14),
48+
@"F15":@(kVK_F15),
49+
@"F16":@(kVK_F16),
50+
@"F17":@(kVK_F17),
51+
@"F18":@(kVK_F18),
52+
@"F19":@(kVK_F19),
53+
@"F20":@(kVK_F20),
54+
@"":@(kVK_ForwardDelete),
55+
@"":@(kVK_Home),
56+
@"":@(kVK_End),
57+
@"":@(kVK_PageUp),
58+
@"":@(kVK_PageDown),
59+
@"":@(kVK_LeftArrow),
60+
@"":@(kVK_RightArrow),
61+
@"":@(kVK_DownArrow),
62+
@"":@(kVK_UpArrow)
63+
};
64+
});
65+
66+
return keyCodeMap;
67+
}
68+
69+
// Uses code from: http://stackoverflow.com/questions/1918841/how-to-convert-ascii-character-to-cgkeycode
70+
71+
static CGKeyCode keyCodeForChar(const char c) {
72+
static CFMutableDictionaryRef charToCodeDict = NULL;
73+
CGKeyCode code;
74+
UniChar character = c;
75+
CFStringRef charStr = NULL;
76+
77+
if (charToCodeDict == NULL) {
78+
size_t i;
79+
charToCodeDict = CFDictionaryCreateMutable(kCFAllocatorDefault,
80+
128,
81+
&kCFCopyStringDictionaryKeyCallBacks,
82+
NULL);
83+
if (charToCodeDict == NULL) return UINT16_MAX;
84+
85+
for (i = 0; i < 128; ++i) {
86+
CFStringRef string = createStringForKey((CGKeyCode)i);
87+
if (string != NULL) {
88+
CFDictionaryAddValue(charToCodeDict, string, (const void *)i);
89+
CFRelease(string);
90+
}
91+
}
92+
}
93+
94+
charStr = CFStringCreateWithCharacters(kCFAllocatorDefault, &character, 1);
95+
96+
if (!CFDictionaryGetValueIfPresent(charToCodeDict, charStr, (const void **)&code)) {
97+
code = UINT16_MAX;
98+
}
99+
100+
CFRelease(charStr);
101+
102+
return code;
103+
}
104+
105+
static CFStringRef createStringForKey(CGKeyCode keyCode) {
106+
TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
107+
CFDataRef layoutData = TISGetInputSourceProperty(currentKeyboard,
108+
kTISPropertyUnicodeKeyLayoutData);
109+
110+
const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData);
111+
112+
UInt32 keysDown = 0;
113+
UniChar chars[4];
114+
UniCharCount realLength;
115+
116+
UCKeyTranslate(keyboardLayout,
117+
keyCode,
118+
kUCKeyActionDisplay,
119+
0,
120+
LMGetKbdType(),
121+
kUCKeyTranslateNoDeadKeysBit,
122+
&keysDown,
123+
sizeof(chars) / sizeof(chars[0]),
124+
&realLength,
125+
chars);
126+
CFRelease(currentKeyboard);
127+
128+
return CFStringCreateWithCharacters(kCFAllocatorDefault, chars, 1);
129+
}
130+
131+
@end

CodingKeys/data/keys.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[
22
{
33
"command": "Move Line Up",
4-
"key": "⌃ ⌥ ",
4+
"key": "⌃ ⌥ J",
55
"mapping":
66
{
77
"Xcode": "⌃ ⌥ ↑",
@@ -20,5 +20,16 @@
2020
"Eclipse": "⌥ ↓",
2121
"Android Studio": ""
2222
}
23+
},
24+
{
25+
"command": "Duplicate Line",
26+
"key": "⌃ ⌥ D",
27+
"mapping":
28+
{
29+
"Xcode": "⌃ ⌥ ↓",
30+
"Sublime Text": "⇧ ⌘ D",
31+
"Eclipse": "⌥ ↓",
32+
"Android Studio": ""
33+
}
2334
}
2435
]

0 commit comments

Comments
 (0)