spacenav_plus/
lib.rs

1use lazy_static::lazy_static;
2use libspnav_bindings as libspnav;
3use std::convert::{From, Into, TryFrom};
4use std::sync::Mutex;
5
6#[derive(Debug, Clone, Copy)]
7pub enum EventType {
8    Any,
9    Motion,
10    Button,
11}
12
13const SPNAV_EVENT_ANY: i32 = 0;
14const SPNAV_EVENT_MOTION: i32 = 1;
15const SPNAV_EVENT_BUTTON: i32 = 2;
16
17impl Into<i32> for EventType {
18    fn into(self) -> i32 {
19        match self {
20            EventType::Any => SPNAV_EVENT_ANY,
21            EventType::Motion => SPNAV_EVENT_MOTION,
22            EventType::Button => SPNAV_EVENT_BUTTON,
23        }
24    }
25}
26
27#[derive(Debug, Clone)]
28pub enum Event {
29    Motion(MotionEvent),
30    Button(ButtonEvent),
31}
32
33#[derive(Debug, Clone)]
34pub struct MotionEvent {
35    pub x: i32,
36    pub y: i32,
37    pub z: i32,
38    pub rx: i32,
39    pub ry: i32,
40    pub rz: i32,
41    pub period: u32,
42    // data[6] not included, because I'm not sure if its redundant
43}
44
45impl MotionEvent {
46    // Convenience method that returns x, y, z translation
47    pub fn t(&self) -> (i32, i32, i32) {
48        (self.x, self.y, self.z)
49    }
50    // Convenience method that returns  rx, ry, rz rotation
51    pub fn r(&self) -> (i32, i32, i32) {
52        (self.rx, self.ry, self.rz)
53    }
54}
55
56impl From<libspnav::spnav_event_motion> for MotionEvent {
57    fn from(event: libspnav::spnav_event_motion) -> Self {
58        MotionEvent {
59            x: event.x,
60            y: event.y,
61            z: event.z,
62            rx: event.rx,
63            ry: event.ry,
64            rz: event.rz,
65            period: event.period,
66        }
67    }
68}
69
70#[derive(Debug, Clone)]
71pub struct ButtonEvent {
72    pub press: bool,
73    pub bnum: i32,
74}
75
76impl From<libspnav::spnav_event_button> for ButtonEvent {
77    fn from(event: libspnav::spnav_event_button) -> Self {
78        ButtonEvent {
79            press: event.press != 0,
80            bnum: event.bnum,
81        }
82    }
83}
84
85impl TryFrom<libspnav::spnav_event> for Event {
86    type Error = ();
87    fn try_from(event: libspnav::spnav_event) -> Result<Self, Self::Error> {
88        unsafe {
89            match event {
90                libspnav::spnav_event {
91                    type_: SPNAV_EVENT_MOTION,
92                } => Ok(Event::Motion(event.motion.into())),
93                libspnav::spnav_event {
94                    type_: SPNAV_EVENT_BUTTON,
95                } => Ok(Event::Button(event.button.into())),
96                _ => Err(()),
97            }
98        }
99    }
100}
101
102#[derive(Debug)]
103pub struct Connection {
104    pub fd: i32,
105}
106
107lazy_static! {
108    static ref CONN_COUNT: Mutex<usize> = Mutex::new(0);
109}
110
111impl Connection {
112    pub fn new() -> Result<Connection, ()> {
113        let mut count = CONN_COUNT.lock().expect("to lock");
114        if *count > 0 {
115            *count += 1;
116            Ok(Connection {
117                fd: lib::spnav_fd()?,
118            })
119        } else {
120            *count = 1;
121            lib::spnav_open()?;
122            Ok(Connection {
123                fd: lib::spnav_fd()?,
124            })
125        }
126    }
127    pub fn poll(&self) -> Option<Event> {
128        lib::spnav_poll_event()
129    }
130    pub fn wait(&self) -> Result<Event, ()> {
131        lib::spnav_wait_event()
132    }
133}
134
135impl Drop for Connection {
136    fn drop(&mut self) {
137        let mut count = CONN_COUNT.lock().expect("to lock");
138        if *count == 1 {
139            *count = 0;
140            lib::spnav_close().expect("to close");
141        } else {
142            *count -= 1;
143        }
144    }
145}
146
147pub mod lib {
148    use super::*;
149
150    /* Open connection to the daemon via AF_UNIX socket.
151     * The unix domain socket interface is an alternative to the original magellan
152     * protocol, and it is *NOT* compatible with the 3D connexion driver. If you wish
153     * to remain compatible, use the X11 protocol (spnav_x11_open, see below).
154     */
155    pub fn spnav_open() -> Result<(), ()> {
156        unsafe {
157            if libspnav::spnav_open() == -1 {
158                Err(())
159            } else {
160                Ok(())
161            }
162        }
163    }
164
165    /* Close connection to the daemon. Use it for X11 or AF_UNIX connections.
166     * Returns -1 on failure
167     */
168    // int spnav_close(void);
169    pub fn spnav_close() -> Result<(), ()> {
170        unsafe {
171            if libspnav::spnav_close() == -1 {
172                Err(())
173            } else {
174                Ok(())
175            }
176        }
177    }
178
179    /* Retrieves the file descriptor used for communication with the daemon, for
180     * use with select() by the application, if so required.
181     * If the X11 mode is used, the socket used to communicate with the X server is
182     * returned, so the result of this function is always reliable.
183     * If AF_UNIX mode is used, an error is returned if
184     * no connection is open / failure occured.
185     */
186    // int spnav_fd(void);
187    pub fn spnav_fd() -> Result<i32, ()> {
188        unsafe {
189            let fd = libspnav::spnav_fd();
190            if fd == -1 {
191                Err(())
192            } else {
193                Ok(fd)
194            }
195        }
196    }
197
198    /* TODO: document */
199    // int spnav_sensitivity(double sens);
200    pub fn spnav_sensitivity(sens: f64) -> Result<i32, ()> {
201        unsafe {
202            let v = libspnav::spnav_sensitivity(sens);
203            if v == -1 {
204                Err(())
205            } else {
206                Ok(v)
207            }
208        }
209    }
210
211    /* blocks waiting for space-nav events. returns 0 if an error occurs */
212    // int spnav_wait_event(spnav_event *event);
213    pub fn spnav_wait_event() -> Result<Event, ()> {
214        let mut event = libspnav::spnav_event {
215            type_: SPNAV_EVENT_ANY,
216        };
217        let t = unsafe { libspnav::spnav_wait_event(&mut event) };
218        if t == 0 {
219            Err(())
220        } else {
221            event.try_into()
222        }
223    }
224
225    /* checks the availability of space-nav events (non-blocking)
226     * returns the event type if available, or 0 otherwise.
227     */
228    // int spnav_poll_event(spnav_event *event);
229    pub fn spnav_poll_event() -> Option<Event> {
230        let mut event = libspnav::spnav_event {
231            type_: SPNAV_EVENT_ANY,
232        };
233        let t = unsafe { libspnav::spnav_poll_event(&mut event) };
234        if t == 0 {
235            None
236        } else {
237            event.try_into().ok()
238        }
239    }
240
241    /* Removes any pending events from the specified type, or all pending events
242     * events if the type argument is SPNAV_EVENT_ANY. Returns the number of
243     * removed events.
244     */
245    // int spnav_remove_events(int type);
246    pub fn spnav_remove_events(t: EventType) -> i32 {
247        unsafe { libspnav::spnav_remove_events(t.into()) }
248    }
249}
250
251#[cfg(test)]
252mod test {
253    use super::*;
254
255    #[test]
256    fn basic() -> Result<(), ()> {
257        let c = Connection::new()?;
258        println!("{:?}", c);
259        println!("{:?}", c.wait());
260        Ok(())
261    }
262}