如何将ASCII字符转换为CGKeyCode?
我需要一个函数,给定一个字符,返回与当前键盘布局上该字符位置相关联的CGKeyCode
。 例如,给定“b”,如果使用US QWERTY,它应该返回kVK_ANSI_B
kVK_ANSI_N
如果使用Dvorak,则返回kVK_ANSI_B
。
为此,Win32 API具有函数VkKeyScan()
。 X11具有函数XStringToKeySym()
。 在CG API中有这样一个函数吗?
我需要这个为了传递一个参数到CGEventCreateKeyboardEvent()
。 我已经尝试使用CGEventKeyboardSetUnicodeString()
来代替,但显然不支持修饰符标志(我需要)。
我已经广泛search这个,但找不到一个体面的答案。 目前我正在使用下面的代码( 可以在线find ),它可以工作,但并不完美(而且很难破译如何简化),我不想在生产代码中使用它:
#include <stdint.h> #include <stdio.h> #include <ApplicationServices/ApplicationServices.h> CGKeyCode keyCodeForCharWithLayout(const char c, const UCKeyboardLayout *uchrHeader); CGKeyCode keyCodeForChar(const char c) { CFDataRef currentLayoutData; TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource(); if (currentKeyboard == NULL) { fputs("Could not find keyboard layout\n", stderr); return UINT16_MAX; } currentLayoutData = TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData); CFRelease(currentKeyboard); if (currentLayoutData == NULL) { fputs("Could not find layout data\n", stderr); return UINT16_MAX; } return keyCodeForCharWithLayout(c, (const UCKeyboardLayout *)CFDataGetBytePtr(currentLayoutData)); } /* Beware! Messy, incomprehensible code ahead! * TODO: XXX: FIXME! Please! */ CGKeyCode keyCodeForCharWithLayout(const char c, const UCKeyboardLayout *uchrHeader) { uint8_t *uchrData = (uint8_t *)uchrHeader; UCKeyboardTypeHeader *uchrKeyboardList = uchrHeader->keyboardTypeList; /* Loop through the keyboard type list. */ ItemCount i, j; for (i = 0; i < uchrHeader->keyboardTypeCount; ++i) { /* Get a pointer to the keyToCharTable structure. */ UCKeyToCharTableIndex *uchrKeyIX = (UCKeyToCharTableIndex *) (uchrData + (uchrKeyboardList[i].keyToCharTableIndexOffset)); /* Not sure what this is for but it appears to be a safeguard... */ UCKeyStateRecordsIndex *stateRecordsIndex; if (uchrKeyboardList[i].keyStateRecordsIndexOffset != 0) { stateRecordsIndex = (UCKeyStateRecordsIndex *) (uchrData + (uchrKeyboardList[i].keyStateRecordsIndexOffset)); if ((stateRecordsIndex->keyStateRecordsIndexFormat) != kUCKeyStateRecordsIndexFormat) { stateRecordsIndex = NULL; } } else { stateRecordsIndex = NULL; } /* Make sure structure is a table that can be searched. */ if ((uchrKeyIX->keyToCharTableIndexFormat) != kUCKeyToCharTableIndexFormat) { continue; } /* Check the table of each keyboard for character */ for (j = 0; j < uchrKeyIX->keyToCharTableCount; ++j) { UCKeyOutput *keyToCharData = (UCKeyOutput *)(uchrData + (uchrKeyIX->keyToCharTableOffsets[j])); /* Check THIS table of the keyboard for the character. */ UInt16 k; for (k = 0; k < uchrKeyIX->keyToCharTableSize; ++k) { /* Here's the strange safeguard again... */ if ((keyToCharData[k] & kUCKeyOutputTestForIndexMask) == kUCKeyOutputStateIndexMask) { long keyIndex = (keyToCharData[k] & kUCKeyOutputGetIndexMask); if (stateRecordsIndex != NULL && keyIndex <= (stateRecordsIndex->keyStateRecordCount)) { UCKeyStateRecord *stateRecord = (UCKeyStateRecord *) (uchrData + (stateRecordsIndex->keyStateRecordOffsets[keyIndex])); if ((stateRecord->stateZeroCharData) == c) { return (CGKeyCode)k; } } else if (keyToCharData[k] == c) { return (CGKeyCode)k; } } else if (((keyToCharData[k] & kUCKeyOutputTestForIndexMask) != kUCKeyOutputSequenceIndexMask) && keyToCharData[k] != 0xFFFE && keyToCharData[k] != 0xFFFF && keyToCharData[k] == c) { return (CGKeyCode)k; } } } } return UINT16_MAX; }
是否有。)(最好是)我忽略的一个标准函数,或者b。)(几乎可以肯定)一个更优雅的方式写我自己的?
这是我最终使用的。 更干净。
#include <CoreFoundation/CoreFoundation.h> #include <Carbon/Carbon.h> /* For kVK_ constants, and TIS functions. */ /* Returns string representation of key, if it is printable. * Ownership follows the Create Rule; that is, it is the caller's * responsibility to release the returned object. */ CFStringRef createStringForKey(CGKeyCode keyCode) { TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource(); CFDataRef layoutData = TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData); const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData); UInt32 keysDown = 0; UniChar chars[4]; UniCharCount realLength; UCKeyTranslate(keyboardLayout, keyCode, kUCKeyActionDisplay, 0, LMGetKbdType(), kUCKeyTranslateNoDeadKeysBit, &keysDown, sizeof(chars) / sizeof(chars[0]), &realLength, chars); CFRelease(currentKeyboard); return CFStringCreateWithCharacters(kCFAllocatorDefault, chars, 1); } /* Returns key code for given character via the above function, or UINT16_MAX * on error. */ CGKeyCode keyCodeForChar(const char c) { static CFMutableDictionaryRef charToCodeDict = NULL; CGKeyCode code; UniChar character = c; CFStringRef charStr = NULL; /* Generate table of keycodes and characters. */ if (charToCodeDict == NULL) { size_t i; charToCodeDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 128, &kCFCopyStringDictionaryKeyCallBacks, NULL); if (charToCodeDict == NULL) return UINT16_MAX; /* Loop through every keycode (0 - 127) to find its current mapping. */ for (i = 0; i < 128; ++i) { CFStringRef string = createStringForKey((CGKeyCode)i); if (string != NULL) { CFDictionaryAddValue(charToCodeDict, string, (const void *)i); CFRelease(string); } } } charStr = CFStringCreateWithCharacters(kCFAllocatorDefault, &character, 1); /* Our values may be NULL (0), so we need to use this function. */ if (!CFDictionaryGetValueIfPresent(charToCodeDict, charStr, (const void **)&code)) { code = UINT16_MAX; } CFRelease(charStr); return code; }
+ (NSString *)keyStringFormKeyCode:(CGKeyCode)keyCode { // Proper key detection seems to want a switch statement, unfortunately switch (keyCode) { case 0: return @"a"; case 1: return @"s"; case 2: return @"d"; case 3: return @"f"; case 4: return @"h"; case 5: return @"g"; case 6: return @"z"; case 7: return @"x"; case 8: return @"c"; case 9: return @"v"; // what is 10? case 11: return @"b"; case 12: return @"q"; case 13: return @"w"; case 14: return @"e"; case 15: return @"r"; case 16: return @"y"; case 17: return @"t"; case 18: return @"1"; case 19: return @"2"; case 20: return @"3"; case 21: return @"4"; case 22: return @"6"; case 23: return @"5"; case 24: return @"="; case 25: return @"9"; case 26: return @"7"; case 27: return @"-"; case 28: return @"8"; case 29: return @"0"; case 30: return @"]"; case 31: return @"o"; case 32: return @"u"; case 33: return @"["; case 34: return @"i"; case 35: return @"p"; case 36: return @"RETURN"; case 37: return @"l"; case 38: return @"j"; case 39: return @"'"; case 40: return @"k"; case 41: return @";"; case 42: return @"\\"; case 43: return @","; case 44: return @"/"; case 45: return @"n"; case 46: return @"m"; case 47: return @"."; case 48: return @"TAB"; case 49: return @"SPACE"; case 50: return @"`"; case 51: return @"DELETE"; case 52: return @"ENTER"; case 53: return @"ESCAPE"; // some more missing codes abound, reserved I presume, but it would // have been helpful for Apple to have a document with them all listed case 65: return @"."; case 67: return @"*"; case 69: return @"+"; case 71: return @"CLEAR"; case 75: return @"/"; case 76: return @"ENTER"; // numberpad on full kbd case 78: return @"-"; case 81: return @"="; case 82: return @"0"; case 83: return @"1"; case 84: return @"2"; case 85: return @"3"; case 86: return @"4"; case 87: return @"5"; case 88: return @"6"; case 89: return @"7"; case 91: return @"8"; case 92: return @"9"; case 96: return @"F5"; case 97: return @"F6"; case 98: return @"F7"; case 99: return @"F3"; case 100: return @"F8"; case 101: return @"F9"; case 103: return @"F11"; case 105: return @"F13"; case 107: return @"F14"; case 109: return @"F10"; case 111: return @"F12"; case 113: return @"F15"; case 114: return @"HELP"; case 115: return @"HOME"; case 116: return @"PGUP"; case 117: return @"DELETE"; // full keyboard right side numberpad case 118: return @"F4"; case 119: return @"END"; case 120: return @"F2"; case 121: return @"PGDN"; case 122: return @"F1"; case 123: return @"LEFT"; case 124: return @"RIGHT"; case 125: return @"DOWN"; case 126: return @"UP"; default: return @"Unknown key"; // Unknown key, bail and note that RUI needs improvement //fprintf(stderr, "%ld\tKey\t%c (DEBUG: %d)\n", currenttime, keyCode; //exit(EXIT_FAILURE; } } + (CGKeyCode)keyCodeFormKeyString:(NSString *)keyString { if ([keyString isEqualToString:@"a"]) return 0; if ([keyString isEqualToString:@"s"]) return 1; if ([keyString isEqualToString:@"d"]) return 2; if ([keyString isEqualToString:@"f"]) return 3; if ([keyString isEqualToString:@"h"]) return 4; if ([keyString isEqualToString:@"g"]) return 5; if ([keyString isEqualToString:@"z"]) return 6; if ([keyString isEqualToString:@"x"]) return 7; if ([keyString isEqualToString:@"c"]) return 8; if ([keyString isEqualToString:@"v"]) return 9; // what is 10? if ([keyString isEqualToString:@"b"]) return 11; if ([keyString isEqualToString:@"q"]) return 12; if ([keyString isEqualToString:@"w"]) return 13; if ([keyString isEqualToString:@"e"]) return 14; if ([keyString isEqualToString:@"r"]) return 15; if ([keyString isEqualToString:@"y"]) return 16; if ([keyString isEqualToString:@"t"]) return 17; if ([keyString isEqualToString:@"1"]) return 18; if ([keyString isEqualToString:@"2"]) return 19; if ([keyString isEqualToString:@"3"]) return 20; if ([keyString isEqualToString:@"4"]) return 21; if ([keyString isEqualToString:@"6"]) return 22; if ([keyString isEqualToString:@"5"]) return 23; if ([keyString isEqualToString:@"="]) return 24; if ([keyString isEqualToString:@"9"]) return 25; if ([keyString isEqualToString:@"7"]) return 26; if ([keyString isEqualToString:@"-"]) return 27; if ([keyString isEqualToString:@"8"]) return 28; if ([keyString isEqualToString:@"0"]) return 29; if ([keyString isEqualToString:@"]"]) return 30; if ([keyString isEqualToString:@"o"]) return 31; if ([keyString isEqualToString:@"u"]) return 32; if ([keyString isEqualToString:@"["]) return 33; if ([keyString isEqualToString:@"i"]) return 34; if ([keyString isEqualToString:@"p"]) return 35; if ([keyString isEqualToString:@"RETURN"]) return 36; if ([keyString isEqualToString:@"l"]) return 37; if ([keyString isEqualToString:@"j"]) return 38; if ([keyString isEqualToString:@"'"]) return 39; if ([keyString isEqualToString:@"k"]) return 40; if ([keyString isEqualToString:@";"]) return 41; if ([keyString isEqualToString:@"\\"]) return 42; if ([keyString isEqualToString:@","]) return 43; if ([keyString isEqualToString:@"/"]) return 44; if ([keyString isEqualToString:@"n"]) return 45; if ([keyString isEqualToString:@"m"]) return 46; if ([keyString isEqualToString:@"."]) return 47; if ([keyString isEqualToString:@"TAB"]) return 48; if ([keyString isEqualToString:@"SPACE"]) return 49; if ([keyString isEqualToString:@"`"]) return 50; if ([keyString isEqualToString:@"DELETE"]) return 51; if ([keyString isEqualToString:@"ENTER"]) return 52; if ([keyString isEqualToString:@"ESCAPE"]) return 53; // some more missing codes abound, reserved I presume, but it would // have been helpful for Apple to have a document with them all listed if ([keyString isEqualToString:@"."]) return 65; if ([keyString isEqualToString:@"*"]) return 67; if ([keyString isEqualToString:@"+"]) return 69; if ([keyString isEqualToString:@"CLEAR"]) return 71; if ([keyString isEqualToString:@"/"]) return 75; if ([keyString isEqualToString:@"ENTER"]) return 76; // numberpad on full kbd if ([keyString isEqualToString:@"="]) return 78; if ([keyString isEqualToString:@"="]) return 81; if ([keyString isEqualToString:@"0"]) return 82; if ([keyString isEqualToString:@"1"]) return 83; if ([keyString isEqualToString:@"2"]) return 84; if ([keyString isEqualToString:@"3"]) return 85; if ([keyString isEqualToString:@"4"]) return 86; if ([keyString isEqualToString:@"5"]) return 87; if ([keyString isEqualToString:@"6"]) return 88; if ([keyString isEqualToString:@"7"]) return 89; if ([keyString isEqualToString:@"8"]) return 91; if ([keyString isEqualToString:@"9"]) return 92; if ([keyString isEqualToString:@"F5"]) return 96; if ([keyString isEqualToString:@"F6"]) return 97; if ([keyString isEqualToString:@"F7"]) return 98; if ([keyString isEqualToString:@"F3"]) return 99; if ([keyString isEqualToString:@"F8"]) return 100; if ([keyString isEqualToString:@"F9"]) return 101; if ([keyString isEqualToString:@"F11"]) return 103; if ([keyString isEqualToString:@"F13"]) return 105; if ([keyString isEqualToString:@"F14"]) return 107; if ([keyString isEqualToString:@"F10"]) return 109; if ([keyString isEqualToString:@"F12"]) return 111; if ([keyString isEqualToString:@"F15"]) return 113; if ([keyString isEqualToString:@"HELP"]) return 114; if ([keyString isEqualToString:@"HOME"]) return 115; if ([keyString isEqualToString:@"PGUP"]) return 116; if ([keyString isEqualToString:@"DELETE"]) return 117; if ([keyString isEqualToString:@"F4"]) return 118; if ([keyString isEqualToString:@"END"]) return 119; if ([keyString isEqualToString:@"F2"]) return 120; if ([keyString isEqualToString:@"PGDN"]) return 121; if ([keyString isEqualToString:@"F1"]) return 122; if ([keyString isEqualToString:@"LEFT"]) return 123; if ([keyString isEqualToString:@"RIGHT"]) return 124; if ([keyString isEqualToString:@"DOWN"]) return 125; if ([keyString isEqualToString:@"UP"]) return 126; return 0; //fprintf(stderr, "keyString %s Not Found. Aborting...\n", keyString); //exit(EXIT_FAILURE); }
原始代码从这里: http : //ritter.ist.psu.edu/projects/RUI/macosx/rui.c
你自己的解决scheme也可以在一个小的补丁(转换为CFDataRef
)之后在Qt下正常工作:
更换
CFDataRef layoutData = TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
同
CFDataRef layoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
避免错误:
invalid conversion from 'void*' to 'const __CFData*'
对于像我这样寻找Michael提出的更新版本的人来说,这里是我自己做的(对我来说,它解决了一些段错误的问题,可能是因为垃圾收集器正在使用这个版本) 。
第一个函数来自转换虚拟键码到Unicodestring 。
NSString* keyCodeToString(CGKeyCode keyCode) { TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource(); CFDataRef uchr = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData); const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout*)CFDataGetBytePtr(uchr); if(keyboardLayout) { UInt32 deadKeyState = 0; UniCharCount maxStringLength = 255; UniCharCount actualStringLength = 0; UniChar unicodeString[maxStringLength]; OSStatus status = UCKeyTranslate(keyboardLayout, keyCode, kUCKeyActionDown, 0, LMGetKbdType(), 0, &deadKeyState, maxStringLength, &actualStringLength, unicodeString); if (actualStringLength == 0 && deadKeyState) { status = UCKeyTranslate(keyboardLayout, kVK_Space, kUCKeyActionDown, 0, LMGetKbdType(), 0, &deadKeyState, maxStringLength, &actualStringLength, unicodeString); } if(actualStringLength > 0 && status == noErr) return [[NSString stringWithCharacters:unicodeString length:(NSUInteger)actualStringLength] lowercaseString]; } return nil; } NSNumber* charToKeyCode(const char c) { static NSMutableDictionary* dict = nil; if (dict == nil) { dict = [NSMutableDictionary dictionary]; // For every keyCode size_t i; for (i = 0; i < 128; ++i) { NSString* str = keyCodeToString((CGKeyCode)i); if(str != nil && ![str isEqualToString:@""]) { [dict setObject:[NSNumber numberWithInt:i] forKey:str]; } } } NSString * keyChar = [NSString stringWithFormat:@"%c" , c]; return [dict objectForKey:keyChar]; }
我用NSNumber
得到一个可为空的对象,由charToKeyCode(c)
返回的值然后可以对nil
进行testing,然后用(CGKeyCode)[charToKeyCode(c) intValue]
。