A homemade 3D input device's driver app (MAKMOUSE) listens on a loopback
port; with Preferences > Navigation > "Navigate with a device app on this
computer" on (off by default), the desktop app connects, says hello and
reads JSON lines: axis motion (orbit, pan or zoom by mode), zoom steps and
standard views. The motion goes through navigation::device like a 3D
mouse's, only in the focused window; a HID device the app names is left
to it. Documented in docs/3d-mouse.md.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #3988 (3D mouse: follow the device that moves, not the first one found)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* 3D mouse: follow the device that moves, not the first one found (#3988)
On Windows and macOS the reader opened the first 3D-mouse interface it
could and read it until it failed. A 3Dconnexion Universal Receiver with
nothing paired opens fine, has a valid layout and never fails, so it kept
the reader for good. A SpaceMouse on a cable next to it was never read
("Not calibrated (hid 256f:c652)", every axis at zero).
The reader now opens every candidate and follows the one in use. The
first is followed from the start, as before. Another takes over once it
shows use (an axis off zero or a button down) while the followed one has
shown none for STALE_AFTER, so two devices moved together don't trade
the reading. A candidate that fails is dropped; if it was the followed
one the reading resets and the next is read. With one device the reader
behaves as before, down to the 100 ms read timeout. Several take 10 ms
turns, and reports queue in the system meanwhile.
The selection is `follow()`, written against a small `ReportSource`
trait, so it is tested with simulated devices on every platform: an idle
receiver next to a SpaceMouse, two devices in use at once, and devices
failing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* 3D mouse: a press that makes a device the one in use counts as a press
Since #3825 a HID device's first button report after it connects is its
baseline: a button down in it is held, not pressed. A device taking over
from another one was connected at that moment, so the press that took
over was swallowed. With an idle Universal Receiver found first, that is
the first button press on the SpaceMouse.
follow() now keeps the buttons each candidate last reported while another
was read, and the reader hands them to the reading as the baseline when
that candidate takes over. A device that reported no buttons before it
took over keeps the held-not-pressed rule.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Tyler Chiriboga <18270301+Tyler-Chiriboga@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>