سؤال

أحتاج إلى وظيفة، بالنظر إلى شخصية، إرجاع CGKeyCode المرتبطة بموقف هذه الشخصية على تخطيط لوحة المفاتيح الحالية. على سبيل المثال، بالنظر إلى "B"، يجب أن تعود kVK_ANSI_B إذا كنت تستخدم الولايات المتحدة QWERTY، أو kVK_ANSI_N إذا كنت تستخدم dvorak.

Win32 API لديه وظيفة VkKeyScan() لهذا الغرض؛ X11 لديه وظيفة XStringToKeySym(). وبعد هل هناك مثل هذه الوظيفة في CG API؟

أحتاج إلى هذا من أجل تمرير معلمة ل CGEventCreateKeyboardEvent(). وبعد لقد حاولت استخدام CGEventKeyboardSetUnicodeString() بدلا من ذلك، ولكن ذلك على ما يبدو لا يدعم أعلام المعدل (التي أحتاجها).

لقد بحثت على نطاق واسع على هذا ولكن لا يمكن العثور على إجابة لائقة. حاليا أنا أستخدم التعليمات البرمجية التالية (وجدت على الانترنت)، والذي يعمل، ولكنه ليس أنيقا تماما (ومن الصعب فك تشفير كيفية تبسيط) وأفضل عدم استخدامه في رمز الإنتاج:

#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;
}

هل هناك.) (يفضل) وظيفة قياسية أطلني، أو ب.) (بالتأكيد تقريبا) بطريقة أكثر أناقة اكتب نفسي؟

هل كانت مفيدة؟

المحلول

هذا هو ما انتهى بي الأمر باستخدام. الكثير منظف.

#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.

الحل الخاص بك يعمل بشكل جيد تحت QT أيضا بعد تصحيح صغير (صب CFDataRef):

استبدال

CFDataRef layoutData = TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);

مع

CFDataRef layoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);

يتجنب الخطأ:

invalid conversion from 'void*' to 'const __CFData*'

لأولئك مثلي الذي بحث عن أكثر حتى الآن إصدار ما اقترح مايكل، إليك ما انتهى به الأمر بنفسي (بالنسبة لي حل بعض قضية Segfault، ربما لأن جامع القمامة يقوم بعمله مع هذا الإصدار).

الوظيفة الأولى تأتي من تحويل رمز المفتاح الظاهري إلى سلسلة Unicode.

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 ثم الوصول إليها (CGKeyCode)[charToKeyCode(c) intValue].

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top