SpaceMouse: on Linux, default to the driver's object-mode axes (#1292)
Without a 3DxWare profile, Linux now uses the driver's object-mode mapping (physical Ry and Rz swapped and reversed), so a tilt rolls and a twist spins. Windows and macOS are unchanged; a profile still takes precedence. Fixes #1172.
This commit is contained in:
1 parent
1473893b08
commit
873a65fd4d
1 file changed
+67
-2
@@ -48,6 +48,21 @@ impl Default for DriverMapping {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl DriverMapping {
|
||||||
|
/// The mapping used when no 3DxWare profile is found. On Linux there is no 3DxWare, so it is
|
||||||
|
/// what the driver applies by default in object mode (#1172): the physical Ry and Rz swapped
|
||||||
|
/// and reversed, so a tilt left/right rolls and a twist spins. Elsewhere the driver writes a
|
||||||
|
/// profile, and the raw axes are used until it does.
|
||||||
|
fn without_profile() -> Self {
|
||||||
|
let mut mapping = Self::default();
|
||||||
|
if cfg!(target_os = "linux") {
|
||||||
|
mapping.axes[4] = AxisRule { output: 5, enabled: true, reversed: true, scale: 1.0 };
|
||||||
|
mapping.axes[5] = AxisRule { output: 4, enabled: true, reversed: true, scale: 1.0 };
|
||||||
|
}
|
||||||
|
mapping
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||||
struct Motion {
|
struct Motion {
|
||||||
translation: [f64; 3],
|
translation: [f64; 3],
|
||||||
@@ -377,7 +392,12 @@ impl SpaceMouse {
|
|||||||
if let Err(error) = std::thread::Builder::new().name("solidcraft-3d-mouse".into()).spawn(move || reader(worker_ctx, worker_state)) {
|
if let Err(error) = std::thread::Builder::new().name("solidcraft-3d-mouse".into()).spawn(move || reader(worker_ctx, worker_state)) {
|
||||||
log::warn!("3D mouse: reader could not start: {error}");
|
log::warn!("3D mouse: reader could not start: {error}");
|
||||||
}
|
}
|
||||||
Self { shared, last_frame: now, driver_mapping: load_driver_mapping().unwrap_or_default(), mapping_checked_at: now }
|
Self {
|
||||||
|
shared,
|
||||||
|
last_frame: now,
|
||||||
|
driver_mapping: load_driver_mapping().unwrap_or_else(DriverMapping::without_profile),
|
||||||
|
mapping_checked_at: now,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tick(&mut self, app: &mut SolveApp, ctx: &Context) {
|
pub fn tick(&mut self, app: &mut SolveApp, ctx: &Context) {
|
||||||
@@ -385,7 +405,7 @@ impl SpaceMouse {
|
|||||||
let seconds = now.duration_since(self.last_frame).as_secs_f64().min(0.05);
|
let seconds = now.duration_since(self.last_frame).as_secs_f64().min(0.05);
|
||||||
self.last_frame = now;
|
self.last_frame = now;
|
||||||
if now.duration_since(self.mapping_checked_at) >= RETRY_AFTER {
|
if now.duration_since(self.mapping_checked_at) >= RETRY_AFTER {
|
||||||
let updated = load_driver_mapping().unwrap_or_default();
|
let updated = load_driver_mapping().unwrap_or_else(DriverMapping::without_profile);
|
||||||
if updated != self.driver_mapping {
|
if updated != self.driver_mapping {
|
||||||
log::info!("3D mouse: applying updated 3DxWare axis profile");
|
log::info!("3D mouse: applying updated 3DxWare axis profile");
|
||||||
self.driver_mapping = updated;
|
self.driver_mapping = updated;
|
||||||
@@ -580,6 +600,51 @@ mod tests {
|
|||||||
assert_eq!(motion.rotation, [1.0, -1.0, 0.0]);
|
assert_eq!(motion.rotation, [1.0, -1.0, 0.0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn without_a_profile_linux_uses_the_drivers_object_mode_axes() {
|
||||||
|
// 3DxWare's object-mode mapping, as a profile (#1172: confirmed on a SpaceMouse Compact).
|
||||||
|
let object_mode = br#"
|
||||||
|
<AppCfg><Devices><Device><AxisBank>
|
||||||
|
<Axis><Enabled>true</Enabled><Input><ActionID>HIDMultiAxis_X</ActionID></Input><Output><ActionID>HIDMultiAxis_X</ActionID><Reversed>false</Reversed></Output></Axis>
|
||||||
|
<Axis><Enabled>true</Enabled><Input><ActionID>HIDMultiAxis_Y</ActionID></Input><Output><ActionID>HIDMultiAxis_Y</ActionID><Reversed>false</Reversed></Output></Axis>
|
||||||
|
<Axis><Enabled>true</Enabled><Input><ActionID>HIDMultiAxis_Z</ActionID></Input><Output><ActionID>HIDMultiAxis_Z</ActionID><Reversed>false</Reversed></Output></Axis>
|
||||||
|
<Axis><Enabled>true</Enabled><Input><ActionID>HIDMultiAxis_Rx</ActionID></Input><Output><ActionID>HIDMultiAxis_Rx</ActionID><Reversed>false</Reversed></Output></Axis>
|
||||||
|
<Axis><Enabled>true</Enabled><Input><ActionID>HIDMultiAxis_Ry</ActionID></Input><Output><ActionID>HIDMultiAxis_Rz</ActionID><Reversed>true</Reversed></Output></Axis>
|
||||||
|
<Axis><Enabled>true</Enabled><Input><ActionID>HIDMultiAxis_Rz</ActionID></Input><Output><ActionID>HIDMultiAxis_Ry</ActionID><Reversed>true</Reversed></Output></Axis>
|
||||||
|
</AxisBank></Device></Devices></AppCfg>
|
||||||
|
"#;
|
||||||
|
let profile = parse_driver_mapping(object_mode).expect("valid 3DxWare profile");
|
||||||
|
if cfg!(target_os = "linux") {
|
||||||
|
assert_eq!(DriverMapping::without_profile(), profile);
|
||||||
|
// Tilting the cap rolls the view and twisting it spins it, as with the driver.
|
||||||
|
let before = Camera::default();
|
||||||
|
let mut tilt = before;
|
||||||
|
navigate(
|
||||||
|
&mut tilt,
|
||||||
|
DriverMapping::without_profile().apply(Axes { translation: [0; 3], rotation: [0, 350, 0] }),
|
||||||
|
0.02,
|
||||||
|
600.0,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
assert_eq!((tilt.yaw, tilt.pitch), (before.yaw, before.pitch));
|
||||||
|
assert_ne!(tilt.roll, before.roll);
|
||||||
|
let mut twist = before;
|
||||||
|
navigate(
|
||||||
|
&mut twist,
|
||||||
|
DriverMapping::without_profile().apply(Axes { translation: [0; 3], rotation: [0, 0, 350] }),
|
||||||
|
0.02,
|
||||||
|
600.0,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
assert_ne!(twist.yaw, before.yaw);
|
||||||
|
assert_eq!(twist.roll, before.roll);
|
||||||
|
} else {
|
||||||
|
assert_eq!(DriverMapping::without_profile(), DriverMapping::default());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn mapped_roll_and_spin_reach_distinct_camera_rotations() {
|
fn mapped_roll_and_spin_reach_distinct_camera_rotations() {
|
||||||
let profile = br#"
|
let profile = br#"
|
||||||
|
|||||||
Reference in new issue
Block a user