1 parent
11a196e6f8
commit
180e62c981
13 files changed
+425
-19
No files matched your search
@@ -0,0 +1,81 @@
|
||||
//! A client of the 3Dconnexion driver (3DxWare) on macOS.
|
||||
//!
|
||||
//! The driver's helper app opens the device exclusively while it runs, so reading the HID device
|
||||
//! directly fails then. The driver offers its own client API instead, in
|
||||
//! `3DconnexionClient.framework`: an app registers with the driver and gets the cap's six axes
|
||||
//! (after the user's 3DxWare settings) and the buttons as messages on a run loop.
|
||||
//!
|
||||
//! The framework is loaded with `dlopen` when [`start`] is called, so nothing links against it and
|
||||
//! the app runs without the driver installed ([`Error::NotInstalled`]). The framework is never
|
||||
//! bundled or redistributed; its declarations here are our own. On other platforms [`start`]
|
||||
//! returns [`Error::Unsupported`].
|
||||
|
||||
#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)]
|
||||
|
||||
use std::fmt;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
mod macos;
|
||||
|
||||
/// What the driver reports.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum Event {
|
||||
/// A 3D mouse was plugged in or the driver connected to one.
|
||||
DeviceAdded,
|
||||
/// The 3D mouse was unplugged or the driver went away.
|
||||
DeviceRemoved,
|
||||
/// The cap moved: push left/right, forward/back, lift/press, then tilt forward/back,
|
||||
/// tilt left/right and twist, each about -350..350 as the HID device reports them.
|
||||
Axes { translation: [i16; 3], rotation: [i16; 3] },
|
||||
/// The buttons that are down, one bit each.
|
||||
Buttons(u32),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Error {
|
||||
/// Not macOS.
|
||||
Unsupported,
|
||||
/// The driver's framework is not installed (or not loadable).
|
||||
NotInstalled,
|
||||
/// The framework lacks a function this client needs (another driver version).
|
||||
MissingSymbol(&'static str),
|
||||
/// The driver refused the handlers (its `OSErr`).
|
||||
Handlers(i16),
|
||||
/// The driver refused the client registration.
|
||||
Register,
|
||||
/// [`start`] was called before.
|
||||
AlreadyStarted,
|
||||
/// The client's thread could not be started.
|
||||
Thread(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Error::Unsupported => write!(f, "the 3Dconnexion driver client only exists on macOS"),
|
||||
Error::NotInstalled => write!(f, "the 3Dconnexion driver is not installed"),
|
||||
Error::MissingSymbol(name) => write!(f, "the 3Dconnexion driver lacks {name}"),
|
||||
Error::Handlers(code) => write!(f, "the 3Dconnexion driver refused the handlers (error {code})"),
|
||||
Error::Register => write!(f, "the 3Dconnexion driver refused the client"),
|
||||
Error::AlreadyStarted => write!(f, "the 3Dconnexion client is already started"),
|
||||
Error::Thread(reason) => write!(f, "the 3Dconnexion client thread did not start: {reason}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
/// Connects to the driver and calls `sink` (on the client's own thread) for everything it reports.
|
||||
/// `name` is how the driver lists this app. Only one client per process; the connection lasts
|
||||
/// until the process ends.
|
||||
pub fn start(name: &str, sink: impl Fn(Event) + Send + Sync + 'static) -> Result<(), Error> {
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
macos::start(name, Box::new(sink))
|
||||
}
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
{
|
||||
let _ = (name, sink);
|
||||
Err(Error::Unsupported)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user