mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-03 09:14:12 +02:00
aaeaff55cc
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>
105 lines
3.4 KiB
C++
105 lines
3.4 KiB
C++
/*
|
|
Copyright 2006-2026 The QElectroTech Team
|
|
This file is part of QElectroTech.
|
|
|
|
QElectroTech is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
QElectroTech is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#ifndef SPACEMOUSEMOTION_H
|
|
#define SPACEMOUSEMOTION_H
|
|
|
|
#include <QtGlobal>
|
|
|
|
/**
|
|
@brief The SpaceMouseSample struct
|
|
One raw motion sample from a SpaceMouseBackend: the device's current
|
|
deflection on its six axes, in whatever units the backend's driver
|
|
reports. With spacenavd's default configuration x is left/right, y is
|
|
forward/back, z is push/pull, and rz is the twist of the cap.
|
|
*/
|
|
struct SpaceMouseSample
|
|
{
|
|
int x = 0, y = 0, z = 0;
|
|
int rx = 0, ry = 0, rz = 0;
|
|
};
|
|
|
|
/**
|
|
@brief The SpaceMouseSettings struct
|
|
The user's 3D mouse motion preferences (Configuration > Souris 3D).
|
|
The defaults reproduce the behaviour before these settings existed.
|
|
*/
|
|
struct SpaceMouseSettings
|
|
{
|
|
enum class ZoomAxis { PushPull, Twist };
|
|
|
|
int pan_speed = 100; ///< percent
|
|
int zoom_speed = 100; ///< percent
|
|
int dead_zone = 0; ///< raw device units, applied to every axis used
|
|
bool invert_pan_x = false;
|
|
bool invert_pan_y = false;
|
|
bool invert_zoom = false;
|
|
ZoomAxis zoom_axis = ZoomAxis::PushPull;
|
|
|
|
static SpaceMouseSettings load();
|
|
void save() const;
|
|
|
|
bool operator==(const SpaceMouseSettings &other) const;
|
|
};
|
|
|
|
/**
|
|
@brief The SpaceMouseViewMotion struct
|
|
What one sample does to a view: a scrollbar delta in pixels, and a
|
|
multiplicative zoom factor (1.0 = no zoom).
|
|
*/
|
|
struct SpaceMouseViewMotion
|
|
{
|
|
qreal scroll_x = 0;
|
|
qreal scroll_y = 0;
|
|
qreal zoom_factor = 1.0;
|
|
};
|
|
|
|
/**
|
|
Pure translation from device samples to view motion, kept free of any
|
|
widget or device so it can be unit-tested (tests/qttest/tst_spacemousemotion).
|
|
*/
|
|
namespace SpaceMouseMotion
|
|
{
|
|
/// Sample period the speeds are calibrated for: about what
|
|
/// spacenavd delivers. A device sampled twice as often moves the
|
|
/// view by half as much per sample, so the speed on screen does not
|
|
/// depend on the event rate of the platform or driver.
|
|
constexpr qreal NOMINAL_PERIOD_MS = 16.0;
|
|
|
|
/// A gap longer than this means the device was at rest before this
|
|
/// sample, not that one sample covers the whole gap.
|
|
constexpr qint64 MAX_PERIOD_MS = 100;
|
|
|
|
/// @return how many nominal periods a sample covers, given the
|
|
/// milliseconds since the previous one: none for a sample in the
|
|
/// same millisecond (it covers no time), one for the first sample
|
|
/// or the first after a rest (elapsed negative or over the maximum)
|
|
qreal stepFor(qint64 elapsed_ms);
|
|
|
|
/// @return \a value with \a dead_zone removed: 0 inside it, and
|
|
/// shifted towards 0 outside it so motion starts smoothly at the edge
|
|
int applyDeadZone(int value, int dead_zone);
|
|
|
|
/// @return what \a sample does to the view, \a elapsed_ms after the
|
|
/// previous sample, under \a settings
|
|
SpaceMouseViewMotion map(const SpaceMouseSample &sample,
|
|
qint64 elapsed_ms,
|
|
const SpaceMouseSettings &settings);
|
|
}
|
|
|
|
#endif // SPACEMOUSEMOTION_H
|