ruabmbua / hidapi-rs

Rust bindings for the hidapi C library
MIT License
165 stars 79 forks source link

Support for monitoring of new devices #155

Open Nemo157 opened 2 months ago

Nemo157 commented 2 months ago

I have a usecase where I need to detect when new matching devices are inserted on the system. Currently I just periodically .refresh_devices() and re-iterate the list looking if there are any I haven't seen before, but this means there is a delay between when the device is inserted and the periodic refresh happens. It would be better if hidapi could monitor for changes in the device list and emit events as new devices appear (and events as they are removed could be useful too, though I already notice these when the Device::read returns an error).

I'm not sure if this is something that is supported by the hidapi C backends, but it looks like it should be pretty trivial to build in the linux-native backend using the udev::MonitorBuilder, and I will probably prototype it soon and could submit a PR if you'd accept it with just a single backend implementation.

alvaro-cuesta commented 2 months ago

hidapi itself does not have hotplug support yet (see libusb/hidapi#238) so I guess any monitoring in native backend should be modeled after it, but I'm not sure if they have a stable API defined yet.

I think an approach for this is to use libusb which has hotplug notification support (not in Windows yet though, it's WIP, see libusb/libusb#736). At least this is what node-hid recommends instead of polling (unsure if the recommendation still stands though, but it's because hidapi device list goes through a full bus enumeration).

Check rusb for that. libusb itself recommends using hidapi for HID anyways so I think it's possible to use them in tandem and only refresh hidapi devices when a libusb hotplug notification arrives.

I haven't tested this though and I guess it can introduce race conditions if timing is tight? Maybe you can open the libusb guaranteed-to-be-valid device with wrap_sys_device instead of refreshing the device list?

Nemo157 commented 2 months ago

I just pushed a branch with a quick implementation on the linux-native backend only: https://github.com/ruabmbua/hidapi-rs/compare/main...Nemo157:hidapi-rs:monitor-linux-native

Testing it in my app it works perfectly, although as mentioned in the libusb/hidapi#238 there are race conditions between enumerating and setting up the monitor, afaict these are unavoidable.

I'll take a look at the other backends later.

Nemo157 commented 1 month ago

Just pushed another branch adding on the hidraw backend: https://github.com/ruabmbua/hidapi-rs/compare/main...Nemo157:hidapi-rs:monitor-linux-hidapi

Tested by building a pre-release version of hidapi with the monitoring changes, using this shell.nix:

{ pkgs ? import <nixpkgs> {}}:

pkgs.mkShell (with pkgs; {
  nativeBuildInputs = [ pkg-config ];
  buildInputs = [
    udev
    libusb1
    (hidapi.overrideAttrs rec {
      version = "connection-callback";
      src = fetchFromGitHub {
        owner = "libusb";
        repo = "hidapi";
        rev = "${version}";
        sha256 = "sha256-peHXQbqNWL3XDQdRJ5piWEcglq742rez816lnja6wZA=";
      };
    })
  ];
})

And running the example with cargo run --no-default-features --features linux-shared-hidraw --example monitor. (Could probably update the submodule to that branch and use linux-static-hidraw instead I guess).