Introducing MPModifiedKey to encapulate keyCode and modifier flags

This commit is contained in:
michael starke
2017-01-25 13:15:24 +01:00
parent e8d5e30142
commit 72574e730a
6 changed files with 65 additions and 48 deletions

View File

@@ -17,12 +17,12 @@
} }
- (void)execute { - (void)execute {
CGKeyCode keyCode = [MPKeyMapper keyCodeForCharacter:@"a" modifier:NULL]; MPModifiedKey key = [MPKeyMapper modifiedKeyForCharacter:@"a"];
if(keyCode == kMPUnknownKeyCode) { if(key.keyCode == kMPUnknownKeyCode) {
NSLog(@"Unable to generate key code for 'A'"); NSLog(@"Unable to generate key code for 'A'");
return; return;
} }
[self sendPressKey:keyCode modifierFlags:kCGEventFlagMaskCommand]; [self sendPressKey:key.keyCode modifierFlags:kCGEventFlagMaskCommand];
[self sendPressKey:kVK_Delete modifierFlags:0]; [self sendPressKey:kVK_Delete modifierFlags:0];
} }

View File

@@ -238,17 +238,16 @@ static CGKeyCode kMPFunctionKeyCodes[] = { kVK_F1, kVK_F2, kVK_F3, kVK_F4, kVK_F
NSUInteger part = random() % 2; NSUInteger part = random() % 2;
unichar key = [pasteContent characterAtIndex:i]; unichar key = [pasteContent characterAtIndex:i];
CGKeyCode keyCode = [MPKeyMapper keyCodeForCharacter:[NSString stringWithFormat:@"%c", key] modifier:NULL]; MPModifiedKey mKey = [MPKeyMapper modifiedKeyForCharacter:[NSString stringWithFormat:@"%c", key]];
/* append unknown keycodes to the paste since we can't type them */ /* append unknown keycodes to the paste since we can't type them */
if (part == 0 || keyCode == kMPUnknownKeyCode) { if (part == 0 || mKey.keyCode == kMPUnknownKeyCode) {
paste = [paste stringByAppendingFormat:@"%c", key]; paste = [paste stringByAppendingFormat:@"%c", key];
[typeKeys addObject:@(kVK_RightArrow)]; [typeKeys addObject:@(kVK_RightArrow)];
[modifiers addObject:@0]; [modifiers addObject:@0];
} }
else { else {
[typeKeys addObject:@(keyCode)]; [typeKeys addObject:@(mKey.keyCode)];
if ([[NSCharacterSet uppercaseLetterCharacterSet] characterIsMember:key]) if ([[NSCharacterSet uppercaseLetterCharacterSet] characterIsMember:key])
[modifiers addObject:@(kCGEventFlagMaskShift)]; [modifiers addObject:@(kCGEventFlagMaskShift)];
@@ -398,11 +397,11 @@ static CGKeyCode kMPFunctionKeyCodes[] = { kVK_F1, kVK_F2, kVK_F3, kVK_F4, kVK_F
} }
- (void)sendPasteKeyCode { - (void)sendPasteKeyCode {
CGKeyCode keyCode = [MPKeyMapper keyCodeForCharacter:@"v" modifier:NULL]; MPModifiedKey mKey = [MPKeyMapper modifiedKeyForCharacter:@"v"];
if(keyCode == kMPUnknownKeyCode) { if(mKey.keyCode == kMPUnknownKeyCode) {
return; // We did not find a mapping for "V" return; // We did not find a mapping for "V"
} }
[self sendPressKey:keyCode modifierFlags:kCGEventFlagMaskCommand]; [self sendPressKey:mKey.keyCode modifierFlags:kCGEventFlagMaskCommand];
} }
- (void)execute { - (void)execute {

View File

@@ -32,16 +32,15 @@ static CGEventFlags _updateModifierMaskForCurrentDefaults(CGEventFlags modifiers
} }
- (instancetype)initWithModifierMask:(CGEventFlags)modiferMask character:(NSString *)character { - (instancetype)initWithModifierMask:(CGEventFlags)modiferMask character:(NSString *)character {
CGEventFlags modifiers; MPModifiedKey mappedKey = [MPKeyMapper modifiedKeyForCharacter:character];
CGKeyCode mappedKey = [MPKeyMapper keyCodeForCharacter:character modifier:&modifiers]; if(mappedKey.keyCode == kMPUnknownKeyCode) {
if(mappedKey == kMPUnknownKeyCode) {
self = nil; self = nil;
} }
else { else {
if(modifiers && (modiferMask != modifiers)) { if(mappedKey.modifier && (modiferMask != mappedKey.modifier)) {
NSLog(@"Supplied modifiers for character %@ do not match required modifiers", character); NSLog(@"Supplied modifiers for character %@ do not match required modifiers", character);
} }
self = [self initWithModifierMask:modiferMask keyCode:mappedKey]; self = [self initWithModifierMask:modiferMask keyCode:mappedKey.modifier];
} }
return self; return self;
} }

View File

@@ -10,6 +10,18 @@
FOUNDATION_EXTERN uint16_t const kMPUnknownKeyCode; FOUNDATION_EXTERN uint16_t const kMPUnknownKeyCode;
typedef struct {
CGEventFlags modifier;
CGKeyCode keyCode;
} MPModifiedKey;
NS_INLINE MPModifiedKey MPMakeModifiedKey(CGEventFlags modifier, CGKeyCode keyCode) {
MPModifiedKey k;
k.keyCode = keyCode;
k.modifier = modifier;
return k;
}
@interface MPKeyMapper : NSObject @interface MPKeyMapper : NSObject
/** /**
@@ -19,16 +31,16 @@ FOUNDATION_EXTERN uint16_t const kMPUnknownKeyCode;
* @param modifier State of modifier flags being pressed with the key * @param modifier State of modifier flags being pressed with the key
* @return NSString containing the current mapping for the keyCode * @return NSString containing the current mapping for the keyCode
*/ */
+ (NSString *)stringForKey:(CGKeyCode)keyCode modifier:(CGEventFlags)modifier;
+ (NSString *)stringForKey:(CGKeyCode)keyCode; + (NSString *)stringForKey:(CGKeyCode)keyCode;
+ (NSString *)stringForModifiedKey:(MPModifiedKey)modifiedKey;
/** /**
* Determines the keyCode (if possible) for the character. Modifiers might be needed * Determines the modifiedkey (if possible) for the character. Modifiers might be needed
* *
* @param character NSString with a single character to be transformed * @param character NSString with a single character to be transformed
* @param modifier pointer to a modifer structure to return the modifer to use with the key code for the character * @param modifier pointer to a modifer structure to return the modifer to use with the key code for the character
* @return virtual Keycode for the supplied string. If none is found, kMPUnkonwKeyCode is returned * @return ModifiedKey if one was found. If none is found, the returned modifiedKey.keyCode has the value kMPUnkonwKeyCode.
*/ */
+ (CGKeyCode)keyCodeForCharacter:(NSString *)character modifier:(CGEventFlags *)modifer; + (MPModifiedKey)modifiedKeyForCharacter:(NSString *)character;
@end @end

View File

@@ -20,18 +20,19 @@
#import "MPKeyMapper.h" #import "MPKeyMapper.h"
#import <Carbon/Carbon.h> #import <Carbon/Carbon.h>
#define MPSafeSetPointer(pointer, value) if(pointer != 0) { *pointer = value; }
uint16_t const kMPUnknownKeyCode = UINT16_MAX; uint16_t const kMPUnknownKeyCode = UINT16_MAX;
@implementation MPKeyMapper @implementation MPKeyMapper
+ (NSString *)stringForKey:(CGKeyCode)keyCode { + (NSString *)stringForKey:(CGKeyCode)keyCode {
return [self stringForKey:keyCode modifier:0]; return [self stringForModifiedKey:MPMakeModifiedKey(0, keyCode)];
} }
+ (NSString *)stringForKey:(CGKeyCode)keyCode modifier:(CGEventFlags)modifier { + (NSString *)stringForModifiedKey:(MPModifiedKey)modifiedKey {
TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource(); TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
CFDataRef layoutData = TISGetInputSourceProperty(currentKeyboard,kTISPropertyUnicodeKeyLayoutData); CFDataRef layoutData = TISGetInputSourceProperty(currentKeyboard,kTISPropertyUnicodeKeyLayoutData);
@@ -49,21 +50,21 @@ uint16_t const kMPUnknownKeyCode = UINT16_MAX;
uint32_t modifierKeyState = 0; uint32_t modifierKeyState = 0;
if(modifier & kCGEventFlagMaskCommand) { if(modifiedKey.modifier & kCGEventFlagMaskCommand) {
modifierKeyState |= ((cmdKey >> 8 ) & 0xFF); modifierKeyState |= ((cmdKey >> 8 ) & 0xFF);
} }
if(modifier & kCGEventFlagMaskShift) { if(modifiedKey.modifier & kCGEventFlagMaskShift) {
modifierKeyState |= ((shiftKey >> 8) & 0xFF); modifierKeyState |= ((shiftKey >> 8) & 0xFF);
} }
if(modifier & kCGEventFlagMaskAlternate) { if(modifiedKey.modifier & kCGEventFlagMaskAlternate) {
modifierKeyState |= ((optionKey >> 8) & 0xFF); modifierKeyState |= ((optionKey >> 8) & 0xFF);
} }
if(modifier & kCGEventFlagMaskControl) { if(modifiedKey.modifier & kCGEventFlagMaskControl) {
modifierKeyState |= ((controlKey >> 8) & 0xFF); modifierKeyState |= ((controlKey >> 8) & 0xFF);
} }
OSStatus success = 0; OSStatus success = 0;
success = UCKeyTranslate(keyboardLayout, success = UCKeyTranslate(keyboardLayout,
keyCode, modifiedKey.keyCode,
kUCKeyActionDisplay, kUCKeyActionDisplay,
modifierKeyState, modifierKeyState,
LMGetKbdType(), LMGetKbdType(),
@@ -76,7 +77,7 @@ uint16_t const kMPUnknownKeyCode = UINT16_MAX;
return CFBridgingRelease(CFStringCreateWithCharacters(kCFAllocatorDefault, chars, 1)); return CFBridgingRelease(CFStringCreateWithCharacters(kCFAllocatorDefault, chars, 1));
} }
+ (CGKeyCode)keyCodeForCharacter:(NSString *)character modifier:(CGEventFlags *)modifer { + (MPModifiedKey)modifiedKeyForCharacter:(NSString *)character {
static NSMutableDictionary *keyboardCodeDictionary; static NSMutableDictionary *keyboardCodeDictionary;
TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource(); TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
@@ -89,21 +90,32 @@ uint16_t const kMPUnknownKeyCode = UINT16_MAX;
keyboardCodeDictionary = [[NSMutableDictionary alloc] initWithCapacity:2]; keyboardCodeDictionary = [[NSMutableDictionary alloc] initWithCapacity:2];
} }
/* search for current character mapping */ /* search for current character mapping */
NSDictionary<NSString *, NSArray<NSNumber *> *> *charToCodeDict = keyboardCodeDictionary[localizedName]; NSDictionary<NSString *, NSValue *> *charToCodeDict = keyboardCodeDictionary[localizedName];
if(nil == charToCodeDict) { if(nil == charToCodeDict) {
/* Add mapping */ /* Add mapping */
static uint64_t modifierCombinations[] = {
0,
kCGEventFlagMaskShift,
kCGEventFlagMaskAlternate,
kCGEventFlagMaskControl,
(kCGEventFlagMaskShift | kCGEventFlagMaskAlternate),
(kCGEventFlagMaskShift | kCGEventFlagMaskControl),
(kCGEventFlagMaskShift | kCGEventFlagMaskAlternate | kCGEventFlagMaskControl)
};
NSMutableDictionary *tempCharToCodeDict = [[NSMutableDictionary alloc] initWithCapacity:128]; NSMutableDictionary *tempCharToCodeDict = [[NSMutableDictionary alloc] initWithCapacity:128];
/* Generate table of keycodes and characters. */ /* Generate table of keycodes and characters. */
/* Loop through every keycode (0 - 127) to find its current mapping. */ /* Loop through every keycode (0 - 127) to find its current mapping. */
/* Loop throuhg every control key compbination for every virutal key */ /* Loop throuhg every control key compbination for every virutal key */
for(CGKeyCode keyCode = 0; keyCode < 128; ++keyCode) { for(CGKeyCode keyCode = 0; keyCode < 128; ++keyCode) {
uint64_t modifiers[] = { 0, kCGEventFlagMaskShift, kCGEventFlagMaskAlternate, kCGEventFlagMaskControl, kCGEventFlagMaskShift | kCGEventFlagMaskAlternate, kCGEventFlagMaskShift | kCGEventFlagMaskControl, kCGEventFlagMaskShift | kCGEventFlagMaskAlternate | kCGEventFlagMaskControl }; for(int modifierIndex = 0; modifierIndex < sizeof(modifierCombinations); modifierIndex++) {
for(int modifierIndex = 0; modifierIndex < sizeof(modifiers); modifierIndex++) { MPModifiedKey mKey = MPMakeModifiedKey(modifierCombinations[modifierIndex], keyCode);
NSString *string = [self stringForKey:keyCode modifier:modifiers[modifierIndex]]; NSString *string = [self stringForModifiedKey:mKey];
if(string != nil && string.length > 0 && nil == tempCharToCodeDict[string]) { if(string != nil && string.length > 0 && nil == tempCharToCodeDict[string]) {
tempCharToCodeDict[string] = @[@(keyCode), @(modifiers[modifierIndex])]; NSValue *value = [NSValue valueWithBytes:&mKey objCType:@encode(MPModifiedKey)];
tempCharToCodeDict[string] = value;
} }
} }
} }
@@ -111,18 +123,13 @@ uint16_t const kMPUnknownKeyCode = UINT16_MAX;
keyboardCodeDictionary[localizedName] = charToCodeDict; keyboardCodeDictionary[localizedName] = charToCodeDict;
} }
NSString *singleCharacter = [character substringToIndex:1].lowercaseString; NSString *singleCharacter = [character substringToIndex:1].lowercaseString;
NSArray<NSNumber *> *result = charToCodeDict[singleCharacter]; NSValue *result = charToCodeDict[singleCharacter];
if(result) { if(!result) {
if(modifer) { return MPMakeModifiedKey(0, kMPUnknownKeyCode);
*modifer = result[1].unsignedIntegerValue;
} }
/* false positive when no modifier was supplied! */ MPModifiedKey mKey;
return result[0].integerValue; [result getValue:&mKey];
} return mKey;
if(modifer) {
*modifer = 0;
}
return kMPUnknownKeyCode;
} }
@end @end

View File

@@ -19,10 +19,10 @@
@implementation MPTestKeyMapper @implementation MPTestKeyMapper
- (void)testKeyMapper { - (void)testKeyMapper {
CGEventFlags flags = 0;
CGKeyCode keyCode = [MPKeyMapper keyCodeForCharacter:@"A" modifier:&flags]; MPModifiedKey key = [MPKeyMapper modifiedKeyForCharacter:@"A"];
XCTAssertEqual(kVK_ANSI_A, keyCode); XCTAssertEqual(kVK_ANSI_A, key.keyCode);
XCTAssertEqual(kCGEventFlagMaskShift, flags); XCTAssertEqual(kCGEventFlagMaskShift, key.modifier);
/* Test only works for german keyboard layout! /* Test only works for german keyboard layout!
XCTAssertEqualObjects(@"a",[MPKeyMapper stringForKey:kVK_ANSI_A modifier:0]); XCTAssertEqualObjects(@"a",[MPKeyMapper stringForKey:kVK_ANSI_A modifier:0]);