Skip to content

Proposal: Callback-based API for Input Reports #725

@noah-nuebling

Description

@noah-nuebling

Problem

Sending feature reports or output reports and receiving responses is currently well-supported by hidapi.
However, to handle input reports from a device, e.g. to track scroll-wheel-movement, the user has to create a dedicated thread and then poll the device using hid_read().

This is bad in particular on macOS because it already provides a callback-based interface that notifies the user whenever the device issues an input report – IOHIDDeviceRegisterInputReportCallback().
In fact, the macOS implementation of hid_read() is built on top of IOHIDDeviceRegisterInputReportCallback().

That means, when building a callback-system on top of hidapi, on macOS, the control-flow will end up looking something like this:

USB interrupt 
  -> CFRunLoop 
    -> IOHIDDeviceRegisterInputReportCallback() callback 
      -> input_reports queue 
        -> hid_read() 
          -> User's polling loop 
            -> User's callback

We're essentially turning an asynchronous API into a synchronous one, and then turning it back into an asynchronous one through polling.
It seems suboptimal.

Proposal

Therefore I propose a dedicated API for receiving input reports via a callback:

int hid_register_input_report_callback(
    hid_device *dev, 
    void (*callback)(unsigned char *data, size_t length, void *user_data),
    void *user_data
);
int hid_unregister_input_report_callback(hid_device *dev);

To control which thread the callbacks arrive on, you'd need additional interfaces. For macOS, it could look like this:

void hid_darwin_set_run_loop(hid_device *dev, CFRunLoopRef run_loop);

If the thread is left unspecified, the API could spawn its own internal thread to monitor the device and deliver callbacks.

Benefits

With this API, the control-flow on macOS would be much simplified:

Instead of:

USB interrupt 
  -> CFRunLoop 
    -> IOHIDDeviceRegisterInputReportCallback() callback 
      -> input_reports queue 
        -> hid_read() 
          -> User's polling loop 
            -> User's callback

It would be:

USB interrupt 
  -> CFRunLoop 
    -> IOHIDDeviceRegisterInputReportCallback() callback 
      -> hid_register_input_report_callback() callback

Therefore, it should be more efficient.

It would also allow users on macOS to easily have the input reports be delivered on their program's main thread (by simply setting the runLoop to CFRunLoopGetMain()), thereby preventing the need to create and coordinate multiple threads, and potentially making their program less error-prone.

The existing synchronous APIs such as hid_read() could stay exactly as they are.

What about Linux and Windows?

My experience is with macOS, but I think on Linux and Windows, a callback-based API like this would also make hidapi more easy-to-use and efficient for the use cases we discussed, such as tracking scroll-wheel-movement.

As I said, I don't know much about Linux and Windows development, so the specific APIs might have to be adjusted.


Thank you for your time and for maintaining hidapi. It really seems like an exceptional library, and I hope to adopt it in my projects.

Metadata

Metadata

Assignees

No one assigned

    Labels

    APIAPI change, Version 1 stuffenhancementNew feature or request

    Type

    No type

    Projects

    Status

    To do

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions