mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-21 22:04:13 +02:00
8fa58bd609
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>
123 lines
4.0 KiB
C++
123 lines
4.0 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 SPACEMOUSEHID_H
|
|
#define SPACEMOUSEHID_H
|
|
|
|
#include "spacemousemotion.h"
|
|
|
|
#include <QByteArray>
|
|
#include <QList>
|
|
#include <QSet>
|
|
#include <QVector>
|
|
|
|
/**
|
|
Decoding of a 3D mouse's raw USB HID reports, for the backends that read
|
|
the device directly (HidBackend) rather than through a driver that has
|
|
already decoded them (spacenavd). No I/O here, so it is unit-tested from
|
|
recorded bytes (tests/qttest/tst_spacemousehid).
|
|
*/
|
|
namespace SpaceMouseHid
|
|
{
|
|
/// USB vendor ids 3Dconnexion devices use: Logitech's for the older
|
|
/// ones, 3Dconnexion's own for everything since about 2011.
|
|
constexpr unsigned short VENDOR_LOGITECH = 0x046d;
|
|
constexpr unsigned short VENDOR_3DCONNEXION = 0x256f;
|
|
|
|
/// @return whether a HID interface is a 3D mouse: a 3Dconnexion
|
|
/// vendor id and the Generic Desktop "multi-axis controller" usage.
|
|
/// Logitech devices that do not report a usage are matched by
|
|
/// product id instead, from the list of their 3D mice.
|
|
bool isSpaceMouse(unsigned short vendor, unsigned short product,
|
|
unsigned short usage_page, unsigned short usage);
|
|
|
|
/// A vendor and product id as hid_enumerate() takes them, where 0
|
|
/// matches every vendor or every product.
|
|
struct DeviceId
|
|
{
|
|
unsigned short vendor = 0;
|
|
unsigned short product = 0;
|
|
};
|
|
|
|
/// @return what a scan asks hidapi for where hidapi reads each
|
|
/// matching device's name strings from the device itself, as on
|
|
/// Windows (issue #1553): every 3Dconnexion device, and Logitech's
|
|
/// 3D mice by product id, since most other mice and keyboards carry
|
|
/// Logitech's id too.
|
|
QList<DeviceId> scanIds();
|
|
|
|
/// Where one value sits in the reports, per the report descriptor.
|
|
struct Field
|
|
{
|
|
int report_id = 0; ///< 0 when the device does not number its reports
|
|
int bit_offset = 0; ///< after the report id byte, if any
|
|
int bit_size = 0;
|
|
int logical_min = 0;
|
|
int logical_max = 0;
|
|
bool relative = false;
|
|
|
|
bool isValid() const { return bit_size > 0; }
|
|
};
|
|
|
|
/// What the report descriptor says about the six axes and the buttons.
|
|
struct Layout
|
|
{
|
|
Field axes[6]; ///< x, y, z, rx, ry, rz
|
|
QVector<Field> buttons; ///< button n (0-based) is buttons[n]
|
|
bool numbered_reports = false;
|
|
|
|
bool hasAxes() const;
|
|
};
|
|
|
|
/// @return the layout described by \a descriptor, or one where
|
|
/// hasAxes() is false if it describes no axes (or is empty)
|
|
Layout parseDescriptor(const QByteArray &descriptor);
|
|
|
|
/// @return the fixed layout of the classic 3Dconnexion reports
|
|
/// (1 = translation, 2 = rotation, 3 = button bitmask), used when
|
|
/// the descriptor cannot be read
|
|
Layout fallbackLayout();
|
|
|
|
/**
|
|
@brief The Decoder class
|
|
Turns a stream of raw reports into samples and button presses.
|
|
A device can send translation and rotation in separate reports, so
|
|
the decoder keeps the last value of every axis and reports all six
|
|
each time any of them changes.
|
|
*/
|
|
class Decoder
|
|
{
|
|
public:
|
|
struct Result
|
|
{
|
|
bool motion = false; ///< sample is new
|
|
SpaceMouseSample sample;
|
|
QList<int> pressed; ///< buttons pressed since the previous report
|
|
};
|
|
|
|
explicit Decoder(const Layout &layout = fallbackLayout());
|
|
Result feed(const QByteArray &report);
|
|
|
|
private:
|
|
Layout m_layout;
|
|
int m_axes[6] = {0, 0, 0, 0, 0, 0};
|
|
QSet<int> m_down; ///< buttons currently held
|
|
};
|
|
}
|
|
|
|
#endif // SPACEMOUSEHID_H
|