(Mac) пытается прочитать входные данные с USB-устройства с помощью функции обратного вызова - входные значения перепутаны и нуждаются в NSLog для работы кода - PullRequest
0 голосов
/ 10 ноября 2018

Контекст:

Я пытаюсь написать скрипт, который может полностью отключить / настроить ускорение мыши на Mac. Я не нашел способа блокировать mouseMoved события с помощью Quartz Event Services (CGEventTap), поэтому я попытался использовать API-интерфейсы IOHID более низкого уровня (IOHIDManager / IOHIDDevice), чтобы получить эксклюзивный доступ к устройству и затем создайте и опубликуйте мой собственный CGEvents из входного обратного вызова. У меня очень странные проблемы ...

Остановка ввода мышью от обработки Системой (kIOHIDOptionsTypeSeizeDevice), получение X и Y ввода от мыши при обратном вызове и обновление позиции курсора на основе этого ввода работали нормально, но затем я попытался получить ввод кнопки как ну и все развалилось.


Задача

При настройке функции обратного вызова для устройства с помощью IOHIDDeviceRegisterInputValueCallback я указываю, что хочу получить IOHIDValues с "Usage", равным 48 и 49 (поля данных для дельт положения по осям X и Y соответственно) а также IOHIDValues с "UsagePage" из 9 (поле данных для кнопочного ввода).

Обратный вызов работает нормально в том смысле, что он реагирует на эти и только эти входные события, но когда я пытаюсь получить доступ к "Usage" "UsagePage" и IntegerValue из IOHIDElement, содержащимся в IOHIDValue что обратный вызов дает вещи становятся странными.

IntegerValue IOHIDElement, который обеспечивает обратный вызов после нажатия кнопки мыши, всегда равен нулю. "Usage" и "UsagePage" равны 1 и 48/49 соответственно - по крайней мере, сейчас. Ранее я напечатал те же значения, и это было 48/49 для всего - даже "UsagePage" для кнопки ввода ... («UsagePage» и «Usage» для ввода «Button» должны быть 0 и 9 соответственно - вы можете посмотреть определения различных значений «Usage» и «UsagePage» здесь )

Также мне нужно NSLog что-то, что угодно, в определенных местах функции обратного вызова, чтобы заставить работать указатель мыши на оси Y ... Чем длиннее строка, которую я печатаю, тем более плавным кажется движение быть. Когда я печатаю в других местах, движение по оси Y становится более резким ...

Спасибо за вашу помощь. Я нахожусь в конце моего несуществующего ума.

Да, и если вы хотите критиковать мой код, пожалуйста! Я все еще учусь.


Edit:

Хорошо, я сам не решил проблему, но нашел «правильный» способ изменения ускорения и скорости мыши - IORegistry. Взгляните на этот пост , это может помочь вам разобраться.



Это функция обратного вызова, которая также отправляет CGEvents:

static void Handle_InputValueCallback(void *context, IOReturn result, void *sender, IOHIDValueRef value) {

    double _mouseSpeed = 0.5;

    // getting Usage and UsagePage of the Input
    IOHIDElementRef element = IOHIDValueGetElement(value);
    uint32_t elementUsage = IOHIDElementGetUsage(element);
    uint32_t elementUsagePage = IOHIDElementGetUsagePage(element);

    // reading the current mouse position
    CGEventRef eventForReadingMousePos = CGEventCreate(NULL);
    CGPoint mouseLocation = CGEventGetLocation(eventForReadingMousePos);

    // printing things

    //NSLog(@"value: %@", value);
    //NSLog(@"element: %@", element);
    //int intValue = (int) IOHIDValueGetIntegerValue(value);
    //NSLog(@"intValue: %d", intValue);

    /*
     intValue is 0 when you click a button
     */

    NSLog(@"elementUsage: %d", elementUsage);
    NSLog(@"elementUsagePage: %d", elementUsagePage);
    /*
         ^
     when you print here, it messes up mouse movement on the Y Axis....
     */


    // parsing input based on its Usage / UsagePage

    if (elementUsagePage == 9) { // button

        /*
         This doesn't work. elementUsage and elementUsagePage are always 48 or 49 for any input ("Usage" should be 0 for button input and 48/49 for mouse-position-delta input, "UsagePage" should be 9 for button input and 1 for pos delta... but both fields are always 48/49...) (This is where I have these numbers from: http://www.freebsddiary.org/APC/usb_hid_usages.php )
         */


    } else if (elementUsage == 48) { // x input
        double deltaX = IOHIDValueGetIntegerValue(value) * _mouseSpeed;
        double newX = mouseLocation.x + deltaX;
        CGEventRef event = CGEventCreateMouseEvent(NULL, kCGEventMouseMoved, CGPointMake(newX, mouseLocation.y), 0);
        CGEventPost(kCGHIDEventTap, event);

        CFRelease(event);

    } else if (elementUsage == 49) { // y input
        double deltaY = IOHIDValueGetIntegerValue(value) * _mouseSpeed;
        double newY = mouseLocation.y + deltaY;
        CGEventRef event = CGEventCreateMouseEvent(NULL, kCGEventMouseMoved, CGPointMake(mouseLocation.x, newY), 0);
        CGEventPost(kCGHIDEventTap, event);

        CFRelease(event);



        NSLog(@"some");

        /* You NEED to print something here to make Y input work... I'm so confused... */

    }


    NSLog(@"thing");

    /* You need to print here, too, otherwise y movement is kind of jerky.. But if you print something more complex than some or thing at either one of these spots and don't print at the other, it seems to work fine as well. */



    CFRelease(eventForReadingMousePos);

}

Эта функция регистрирует обратный вызов:

static void registerInputCallbackForDevice(IOHIDDeviceRef device) {

    NSLog(@"registering device: %@", device);
    NSCAssert(device != NULL, @"tried to register a device which equals NULL");

    // create matching dicts
    CFMutableDictionaryRef elementMatchDict1
    = CFDictionaryCreateMutable(kCFAllocatorDefault,2,&kCFTypeDictionaryKeyCallBacks,&kCFTypeDictionaryValueCallBacks);

    CFMutableDictionaryRef elementMatchDict2
    = CFDictionaryCreateMutable(kCFAllocatorDefault,2,&kCFTypeDictionaryKeyCallBacks,&kCFTypeDictionaryValueCallBacks);

    CFMutableDictionaryRef elementMatchDict3
    = CFDictionaryCreateMutable(kCFAllocatorDefault,2,&kCFTypeDictionaryKeyCallBacks,&kCFTypeDictionaryValueCallBacks);

    // creating values for the "elementMatchDicts" - definition of values for keys "Usage" and "UsagePage" here: http://www.freebsddiary.org/APC/usb_hid_usages.php
    int fourtyEight = 48;
    int fourtyNine = 49;
    int nine = 9;
    CFNumberRef UsageX = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &fourtyEight); // "Usage" value for X input
    CFNumberRef UsageY = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &fourtyNine); // "Usage" value for Y input
    CFNumberRef usagePageButton = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &nine); // "UsagePage" for button input

    // filling up the dictionaries
    CFDictionarySetValue(elementMatchDict1, CFSTR("Usage"), UsageX);
    CFDictionarySetValue(elementMatchDict2, CFSTR("Usage"), UsageY);
    CFDictionarySetValue(elementMatchDict3, CFSTR("UsagePage"), usagePageButton);
    CFMutableDictionaryRef matchArrayPrimitive[3] = {elementMatchDict1, elementMatchDict2, elementMatchDict3};
    CFArrayRef matchArray = CFArrayCreate(kCFAllocatorDefault, (const void **)matchArrayPrimitive, 2, NULL);
    IOHIDDeviceSetInputValueMatchingMultiple(device, matchArray);

    IOHIDDeviceRegisterInputValueCallback(device, &Handle_InputValueCallback, NULL);

    CFRelease(elementMatchDict1);
    CFRelease(elementMatchDict2);
    CFRelease(elementMatchDict3);

}

AppDelegate.m - для копирования / вставки.

Вам нужно будет отключить «песочницу» приложения, иначе она выдаст ошибку времени выполнения (также помните, что это сделает вашу мышь непригодной для использования во время ее работы - она ​​должна игнорировать трекпад и магические мыши)

#import "AppDelegate.h"
#import "IOKit/hid/IOHIDManager.h"

@implementation AppDelegate


// global variables
IOHIDManagerRef HIDManager;


- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    setupHIDManagerAndCallbacks();
}

static void setupHIDManagerAndCallbacks() {


    // Create an HID Manager
    HIDManager = IOHIDManagerCreate(kCFAllocatorDefault,
                                    kIOHIDOptionsTypeNone);

    // Create a Matching Dictionaries
    CFMutableDictionaryRef matchDict1
    = CFDictionaryCreateMutable(kCFAllocatorDefault, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    CFMutableDictionaryRef matchDict2
    = CFDictionaryCreateMutable(kCFAllocatorDefault, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    CFMutableDictionaryRef matchDict3
    = CFDictionaryCreateMutable(kCFAllocatorDefault, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);

    // Specify properties of the devices which we want to add to the HID Manager in the Matching Dictionary
    CFArrayRef matches;
    CFDictionarySetValue(matchDict1, CFSTR("PrimaryUsage"), (const void *)0x227);       // add USB mice
    CFDictionarySetValue(matchDict1, CFSTR("Transport"), CFSTR("USB"));                 //
    CFDictionarySetValue(matchDict2, CFSTR("PrimaryUsage"), (const void *)0x227);       // add Bluetooth mice
    CFDictionarySetValue(matchDict2, CFSTR("Transport"), CFSTR("Bluetooth"));           //
    CFDictionarySetValue(matchDict3, CFSTR("PrimaryUsage"), (const void *)0x227);       // add Bluetooth low energy mice (?)
    CFDictionarySetValue(matchDict3, CFSTR("Transport"), CFSTR("BluetoothLowEnergy"));  //

    CFMutableDictionaryRef matchesList[] = {matchDict1, matchDict2, matchDict3};
    matches = CFArrayCreate(kCFAllocatorDefault, (const void **)matchesList, 3, NULL);


    //Register the Matching Dictionary to the HID Manager
    IOHIDManagerSetDeviceMatchingMultiple(HIDManager, matches);

    CFRelease(matches);
    CFRelease(matchDict1);
    CFRelease(matchDict2);
    CFRelease(matchDict3);


    // Register the HID Manager on our app’s run loop
    IOHIDManagerScheduleWithRunLoop(HIDManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode);

    // Open the HID Manager
    IOReturn IOReturn = IOHIDManagerOpen(HIDManager, kIOHIDOptionsTypeSeizeDevice);
    if(IOReturn) NSLog(@"IOHIDManagerOpen failed.");  //  Couldn't open the HID manager! TODO: proper error handling


    // Register callback for USB device detection with the HID Manager, this will invoke Handle_DeviceMatchingCallback for each matching device
    IOHIDManagerRegisterDeviceMatchingCallback(HIDManager, &Handle_DeviceMatchingCallback, NULL);
}


static void Handle_DeviceMatchingCallback (void *context, IOReturn result, void *sender, IOHIDDeviceRef device) {

    if (devicePassesFiltering(device) ) {
        registerInputCallbackForDevice(device);
    }
}
static BOOL devicePassesFiltering(IOHIDDeviceRef HIDDevice) {
    NSString *deviceName = [NSString stringWithUTF8String:
       CFStringGetCStringPtr(IOHIDDeviceGetProperty(HIDDevice, CFSTR("Product")), kCFStringEncodingMacRoman)];
    NSString *deviceNameLower = [deviceName lowercaseString];

    if ([deviceNameLower rangeOfString:@"magic"].location == NSNotFound) {
        return TRUE;
    } else {
        return FALSE;
    }
}


static void registerInputCallbackForDevice(IOHIDDeviceRef device) {

    NSLog(@"registering device: %@", device);
    NSCAssert(device != NULL, @"tried to register a device which equals NULL");

    // create matching dicts
    CFMutableDictionaryRef elementMatchDict1
    = CFDictionaryCreateMutable(kCFAllocatorDefault,2,&kCFTypeDictionaryKeyCallBacks,&kCFTypeDictionaryValueCallBacks);

    CFMutableDictionaryRef elementMatchDict2
    = CFDictionaryCreateMutable(kCFAllocatorDefault,2,&kCFTypeDictionaryKeyCallBacks,&kCFTypeDictionaryValueCallBacks);

    CFMutableDictionaryRef elementMatchDict3
    = CFDictionaryCreateMutable(kCFAllocatorDefault,2,&kCFTypeDictionaryKeyCallBacks,&kCFTypeDictionaryValueCallBacks);

    // values for keys "Usage" and "UsagePage" here http://www.freebsddiary.org/APC/usb_hid_usages.php
    int fourtyEight = 48;
    int fourtyNine = 49;
    int nine = 9;
    CFNumberRef UsageX = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &fourtyEight); // "Usage" value for X input
    CFNumberRef UsageY = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &fourtyNine); // "Usage" value for Y input
    CFNumberRef usagePageButton = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &nine); // "UsagePage" for button input

    CFDictionarySetValue (elementMatchDict1, CFSTR("Usage"), UsageX);
    CFDictionarySetValue (elementMatchDict2, CFSTR("Usage"), UsageY);
    CFDictionarySetValue (elementMatchDict3, CFSTR("UsagePage"), usagePageButton);
    CFMutableDictionaryRef matchArrayPrimitive[3] = {elementMatchDict1, elementMatchDict2, elementMatchDict3};
    CFArrayRef matchArray = CFArrayCreate(kCFAllocatorDefault, (const void **)matchArrayPrimitive, 2, NULL);
    IOHIDDeviceSetInputValueMatchingMultiple(device, matchArray);

    IOHIDDeviceRegisterInputValueCallback(device, &Handle_InputValueCallback, NULL);

    CFRelease(elementMatchDict1);
    CFRelease(elementMatchDict2);
    CFRelease(elementMatchDict3);

}


static void Handle_InputValueCallback(void *context, IOReturn result, void *sender, IOHIDValueRef value) {

    double _mouseSpeed = 0.5;

    IOHIDElementRef element = IOHIDValueGetElement(value);
    uint32_t elementUsage = IOHIDElementGetUsage(element);
    uint32_t elementUsagePage = IOHIDElementGetUsagePage(element);
    CGEventRef eventForReadingMousePos = CGEventCreate(NULL);
    CGPoint mouseLocation = CGEventGetLocation(eventForReadingMousePos);

    //NSLog(@"value: %@", value);
    //NSLog(@"element: %@", element);
    //int intValue = (int) IOHIDValueGetIntegerValue(value);
    //NSLog(@"intValue: %d", intValue);

    /*
     intValue is 0 when you click a button
     */

    NSLog(@"elementUsage: %d", elementUsage);
    NSLog(@"elementUsagePage: %d", elementUsagePage);

    /*
     when you print here, it messes up mouse movement on the Y Axis....
     */



    if (elementUsagePage == 9) { // button

        /*
         This doesn't work. elementUsage and elementUsagePage are always 48 or 49 for any input ("Usage" should be 0 for button input and 48/49 for mouse-position-delta input, "UsagePage" should be 9 for button input and 1 for pos delta... but both fields are always 48/49...) (This is where I have these numbers from: http://www.freebsddiary.org/APC/usb_hid_usages.php )
         */


    } else if (elementUsage == 48) { // x axis
        double deltaX = IOHIDValueGetIntegerValue(value) * _mouseSpeed;
        double newX = mouseLocation.x + deltaX;
        CGEventRef event = CGEventCreateMouseEvent(NULL, kCGEventMouseMoved, CGPointMake(newX, mouseLocation.y), 0);
        CGEventPost(kCGHIDEventTap, event);

        CFRelease(event);

    } else if (elementUsage == 49) { // y axis
        double deltaY = IOHIDValueGetIntegerValue(value) * _mouseSpeed;
        double newY = mouseLocation.y + deltaY;
        CGEventRef event = CGEventCreateMouseEvent(NULL, kCGEventMouseMoved, CGPointMake(mouseLocation.x, newY), 0);
        CGEventPost(kCGHIDEventTap, event);

        CFRelease(event);



        NSLog(@"some");

        /* You NEED to print something here to make Y input work... I'm so confused... */

    }


    NSLog(@"thing");

    /* You need to print here, too, otherwise y movement is kind of jerky.. But if you print something more complex than some or thing at either one of these spots and don't print at the other, it seems to work fine as well. */



    CFRelease(eventForReadingMousePos);

}

@end
...