While no 3D mouse is plugged in, which for most users is always, the
hidapi backend looks for one every 3 seconds for as long as
QElectroTech runs. It asked hidapi for every HID device,
hid_enumerate(0, 0), and on Windows hidapi reads the serial number,
manufacturer and product strings of each device that matches what it
is asked for from the device itself. So every mouse, keyboard, headset
and webcam on the PC was asked for its names every 3 seconds, on the
GUI thread. Issue #1553 reports the pointer moving by itself on Windows
since the first package with this backend; nothing else in that package
talks to devices. SDL 2.0.9 polled the same way and saw freezes and a
keyboard that kept re-initialising (libsdl-org/SDL#3071).
The scan now asks for 3Dconnexion's vendor id, and for each 3D mouse
made under Logitech's id by its product id, since most other mice and
keyboards carry Logitech's id too. That is twelve hidapi calls instead
of one, but a device that does not match is no longer asked for its
names. On Linux and macOS hidapi takes the names from the system, and
one call is cheaper than twelve (on Linux, median of 200: 5 ms against
36 ms), so the scan there is unchanged.
Where the scan is filtered, a Logitech-id device that is not one of the
eleven 3D mice on the list is no longer found, even if it reports
itself as a multi-axis controller. 3Dconnexion's own mice and keyboards,
such as the CadMouse, are still asked: their vendor id is asked for as
a whole, so that new 3D mice are found without a list.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
hidapi's macOS backend opens every device with
kIOHIDOptionsTypeSeizeDevice unless told otherwise (hid_init() calls
hid_darwin_set_open_exclusive(1) for backward compatibility). When
3DxWare is running it already holds the SpacePilot/SpaceMouse, so the
seize fails, hid_open_path() returns NULL and HidBackend::scan() keeps
retrying every 3 s without ever finding the device.
Reported in discussion #599: a SpacePilot Pro (046d:c629) is listed by
hid_enumerate() and works in 3DxWare, but has no effect in QET's macOS
build. Enumeration never opens a device, so it did not exercise this.
Guarded on HID_API_VERSION >= 0.12, where the setter first appeared.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The 3D mouse only worked on Linux, through spacenavd. This adds a second
backend that reads the device directly over USB through hidapi, with no
3Dconnexion driver or SDK: the route to Windows and macOS (discussion
#599), and usable on Linux without spacenavd.
SpaceMouseHid decodes the raw reports from the device's own report
descriptor -- where each axis and button sits, its range, absolute or
relative -- so no per-model table is needed, with the classic report
1/2/3 layout as a fallback when the descriptor cannot be read and the
0x1c button list newer devices send. Absolute axes are rescaled to
+-500 exactly as spacenavd does, so both backends give QET the same
values. HidBackend polls from the main thread (fast while moving, slow
when still), emits one sample per poll, and looks for a device every 3 s
so plugging one in or back in needs no restart.
QET_SPACEMOUSE_BACKEND (auto, spnav, hid) picks the backend; auto keeps
libspnav on Linux when it is found and uses hidapi otherwise. hidapi is
found through pkg-config as hidapi-hidraw (Linux) or hidapi (MSYS2,
Homebrew).
A sample arriving in the same millisecond as the previous one now counts
for no time instead of a full period, so a burst of queued samples no
longer moves the view further than the time it covers.
Tested without a device: tst_spacemousehid (descriptor parsing, broken
and hostile descriptors, every report form, recordings from real devices
once they are added to fixtures/spacemouse), and end to end on Linux
through a virtual USB device created with /dev/uhid: the same moves give
byte-identical screenshots through the hidapi and libspnav backends, an
absolute axis is rescaled as spacenavd does, buttons trigger their
bound action, and unplugging and replugging while QET runs (including
with a dialog open that a device button opened) reconnects cleanly.
Not tested on Windows, macOS or real hardware.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>