Himaprasoon
Himaprasoon

Reputation: 2659

Not able to detect trackpad events on macOS using IOKit hidmanager - IOHIDManagerRegisterInputValueCallback

I am trying to detect mouse events along with device details on macos. The code detects external mouse events but doesn't detect internal trackpad events. Surprisingly the same code works on

It doesn't work on

Code

#include <iostream>
#include <IOKit/hid/IOHIDManager.h>
#include <CoreFoundation/CoreFoundation.h>

void mouseEventCallback(void *context, IOReturn result, void *sender, IOHIDValueRef value) {
    std::cout << "Mouse event" << std::endl; // Prints only external mouse events. Doesn't work for internal trackpad.
}

int main() {
    IOHIDManagerRef manager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
    
    // Create a matching dictionary for mouse and trackpad devices
    CFMutableDictionaryRef matchingDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
        &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    
    // Add matching criteria for mouse and trackpad devices
    CFDictionaryAddValue(matchingDict, CFSTR(kIOHIDDeviceUsagePageKey), CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, (const int[]){1})); // Generic Desktop Controls
    CFDictionaryAddValue(matchingDict, CFSTR(kIOHIDDeviceUsageKey), CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, (const int[]){2, 5})); // Mouse, Touchpad

    // Set the matching criteria
    IOHIDManagerSetDeviceMatching(manager, matchingDict);
    
    // Register the callback for input value changes
    IOHIDManagerRegisterInputValueCallback(manager, mouseEventCallback, NULL);
    
    // Open the HID manager
    IOHIDManagerOpen(manager, kIOHIDOptionsTypeNone);
    
    // Schedule the HID manager with the current run loop
    IOHIDManagerScheduleWithRunLoop(manager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
    
    // Run the loop to listen for events
    CFRunLoopRun();
    
    // Clean up
    CFRelease(matchingDict);
    CFRelease(manager);
    
    return 0;
}

Compile command clang++ -o mouse mouse.cpp -framework IOKit -framework CoreFoundation

I have also tried external libs like https://github.com/icculus/manymouse but the same issue persists. Also, when I use IOHIDManagerRegisterDeviceMatchingCallback both external mouse and trackpad gets registerd but the events are not detected from the internal trackpad.

I tried using CGEventTapCreate which detects events from both trackpad and external mouse on all devices but, I can't use it as it doesn't give device details along with events (Product id, vendor id etc).

// This works
#include <iostream>
#include <ApplicationServices/ApplicationServices.h>

// Event tap callback function
CGEventRef eventTapCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void* userInfo) {

    if (type == kCGEventLeftMouseDown || type == kCGEventRightMouseDown || type == kCGEventOtherMouseDown) {
        CGPoint mouseLocation = CGEventGetLocation(event);
        std::cout << "Mouse Clicked at: (" << mouseLocation.x << ", " << mouseLocation.y << ")" << std::endl;
    }
    else if (type == kCGEventScrollWheel) {
        // For scroll events
        CGPoint scrollDelta = CGEventGetUnflippedLocation(event);
        std::cout << "Mouse Scrolled at: (" << scrollDelta.x << ", " << scrollDelta.y << ")" << std::endl;
    }

    return event;
}

int main() {
    // Create an event tap to monitor mouse events
    CGEventMask eventMask = CGEventMaskBit(kCGEventLeftMouseDown) |
                            CGEventMaskBit(kCGEventRightMouseDown) |
                            CGEventMaskBit(kCGEventOtherMouseDown) |
                            CGEventMaskBit(kCGEventScrollWheel);

    CFMachPortRef eventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionListenOnly, eventMask, eventTapCallback, nullptr);
    if (!eventTap) {
        std::cerr << "Failed to create event tap." << std::endl;
        return 1;
    }

    // Create a run loop source and add it to the current run loop
    CFRunLoopSourceRef runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);
    CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
    CFRelease(runLoopSource);

    // Enable the event tap
    CGEventTapEnable(eventTap, true);

    // Run the run loop to process events
    CFRunLoopRun();

    return 0;
}

Upvotes: 2

Views: 78

Answers (0)

Related Questions