Skip to main content

gilrs_core/
lib.rs

1#[macro_use]
2extern crate log;
3
4use std::fmt;
5use std::fmt::Display;
6use std::fmt::Formatter;
7
8use std::error;
9use std::time::Duration;
10use std::time::SystemTime;
11
12mod platform;
13pub mod utils;
14
15/// True, if Y axis of sticks commonly points downwards.
16pub const IS_Y_AXIS_REVERSED: bool = platform::IS_Y_AXIS_REVERSED;
17
18/// Allow control of gamepad's force feedback.
19#[derive(Debug)]
20pub struct FfDevice {
21    inner: platform::FfDevice,
22}
23
24impl FfDevice {
25    /// Sets magnitude for strong and weak ff motors.
26    pub fn set_ff_state(&mut self, strong: u16, weak: u16, min_duration: Duration) {
27        self.inner.set_ff_state(strong, weak, min_duration)
28    }
29}
30
31/// Holds information about gamepad event.
32#[derive(Copy, Clone, PartialEq, Eq, Debug)]
33#[non_exhaustive]
34pub struct Event {
35    /// Id of gamepad.
36    pub id: usize,
37    /// Event's data.
38    pub event: EventType,
39    /// Time when event was emitted.
40    pub time: SystemTime,
41}
42
43impl Event {
44    /// Creates new event with current time.
45    pub fn new(id: usize, event: EventType) -> Self {
46        let time = utils::time_now();
47        Event { id, event, time }
48    }
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52/// Gamepad event.
53#[non_exhaustive]
54pub enum EventType {
55    ButtonPressed(EvCode),
56    ButtonReleased(EvCode),
57    AxisValueChanged(i32, EvCode),
58    Connected,
59    Disconnected,
60}
61
62/// Holds information about expected axis range and deadzone.
63#[derive(Copy, Clone, Debug)]
64pub struct AxisInfo {
65    pub min: i32,
66    pub max: i32,
67    pub deadzone: Option<u32>,
68}
69
70/// State of device's power supply.
71///
72/// Battery level is reported as integer between 0 and 100.
73///
74/// ## Example
75///
76/// ```
77/// use gilrs_core::PowerInfo;
78/// # let gilrs = gilrs_core::Gilrs::new().unwrap();
79///
80/// match gilrs.gamepad(0).map(|g| g.power_info()) {
81///     Some(PowerInfo::Discharging(lvl)) if lvl <= 10 => println!("Low battery level, you should \
82///                                                           plug your gamepad"),
83///     _ => (),
84/// };
85/// ```
86#[derive(Debug, Copy, Clone, PartialEq, Eq)]
87pub enum PowerInfo {
88    /// Failed to determine power status.
89    Unknown,
90    /// Device doesn't have battery.
91    Wired,
92    /// Device is running on the battery.
93    Discharging(u8),
94    /// Battery is charging.
95    Charging(u8),
96    /// Battery is charged.
97    Charged,
98}
99
100/// Struct used to manage gamepads and retrieve events.
101#[derive(Debug)]
102pub struct Gilrs {
103    inner: platform::Gilrs,
104}
105
106impl Gilrs {
107    pub fn new() -> Result<Self, Error> {
108        let inner = platform::Gilrs::new().map_err(|e| match e {
109            PlatformError::NotImplemented(inner) => Error::NotImplemented(Gilrs { inner }),
110            PlatformError::Other(e) => Error::Other(e),
111        })?;
112
113        Ok(Gilrs { inner })
114    }
115
116    /// Returns oldest event or `None` if all events were processed.
117    pub fn next_event(&mut self) -> Option<Event> {
118        self.inner.next_event()
119    }
120
121    /// Returns oldest event, waiting for new event if necessary.
122    pub fn next_event_blocking(&mut self, timeout: Option<Duration>) -> Option<Event> {
123        self.inner.next_event_blocking(timeout)
124    }
125
126    /// Borrows `Gamepad` or return `None` if index is invalid. Returned gamepad may be disconnected.
127    pub fn gamepad(&self, id: usize) -> Option<&Gamepad> {
128        unsafe {
129            let gp: Option<&platform::Gamepad> = self.inner.gamepad(id);
130
131            gp.map(|gp| &*(gp as *const _ as *const Gamepad))
132        }
133    }
134
135    /// Returns id greater than id of last connected gamepad. The returned value is only hint
136    /// and may be much larger than number of observed gamepads. For example, it may return maximum
137    /// number of connected gamepads on platforms when this limit is small.
138    ///
139    /// `gamepad(id)` should return `Some` if using id that is smaller than value returned from this
140    /// function.
141    pub fn last_gamepad_hint(&self) -> usize {
142        self.inner.last_gamepad_hint()
143    }
144}
145
146/// Provides information about gamepad.
147#[derive(Debug)]
148#[repr(transparent)]
149pub struct Gamepad {
150    inner: platform::Gamepad,
151}
152
153impl Gamepad {
154    /// Returns name of gamepad.
155    pub fn name(&self) -> &str {
156        self.inner.name()
157    }
158
159    /// Returns true if gamepad is connected.
160    pub fn is_connected(&self) -> bool {
161        self.inner.is_connected()
162    }
163
164    /// Returns UUID that represents gamepad model.
165    ///
166    /// Returned UUID should be the same as SLD2 uses. If platform does not provide any method to
167    /// distinguish between gamepad models, nil UUID is returned.
168    ///
169    /// It is recommended to process with the [UUID crate](https://crates.io/crates/uuid).
170    /// Use `Uuid::from_bytes` method to create a `Uuid` from the returned bytes.
171    pub fn uuid(&self) -> [u8; 16] {
172        *self.inner.uuid().as_bytes()
173    }
174
175    /// Returns the vendor ID, as assigned by the USB-IF, when available.
176    pub fn vendor_id(&self) -> Option<u16> {
177        self.inner.vendor_id()
178    }
179
180    /// Returns the product ID, as assigned by the vendor, when available.
181    pub fn product_id(&self) -> Option<u16> {
182        self.inner.product_id()
183    }
184
185    /// The pad's serial number as the input layer reports it, when
186    /// available (azul fork: IOKit's `kIOHIDSerialNumberKey` on macOS;
187    /// `None` on the other backends).
188    pub fn serial(&self) -> Option<&str> {
189        self.inner.serial()
190    }
191
192    /// Returns device's power supply state.
193    pub fn power_info(&self) -> PowerInfo {
194        self.inner.power_info()
195    }
196
197    /// Returns true if force feedback is supported by device,
198    pub fn is_ff_supported(&self) -> bool {
199        self.inner.is_ff_supported()
200    }
201
202    /// Creates `FfDevice` corresponding to this gamepad.
203    pub fn ff_device(&self) -> Option<FfDevice> {
204        self.inner.ff_device().map(|inner| FfDevice { inner })
205    }
206
207    /// Returns slice with EvCodes that may appear in button related events.
208    pub fn buttons(&self) -> &[EvCode] {
209        unsafe {
210            let bt: &[platform::EvCode] = self.inner.buttons();
211
212            &*(bt as *const _ as *const [EvCode])
213        }
214    }
215
216    /// Returns slice with EvCodes that may appear in axis related events.
217    pub fn axes(&self) -> &[EvCode] {
218        unsafe {
219            let ax: &[platform::EvCode] = self.inner.axes();
220
221            &*(ax as *const _ as *const [EvCode])
222        }
223    }
224
225    /// Returns information about a specific axis. `None` may be returned if a device doesn't have an axis
226    /// with provided `EvCode`.
227    pub fn axis_info(&self, nec: EvCode) -> Option<&AxisInfo> {
228        self.inner.axis_info(nec.0)
229    }
230}
231
232#[cfg(target_os = "linux")]
233use std::path::Path;
234
235/// Linux specific extension to `Gamepad`.
236#[cfg(target_os = "linux")]
237pub trait LinuxGamepadExt {
238    /// Returns the device node of gamepad.
239    fn devpath(&self) -> &Path;
240}
241
242#[cfg(target_os = "linux")]
243impl LinuxGamepadExt for Gamepad {
244    /// Returns the device node of gamepad.
245    fn devpath(&self) -> &Path {
246        Path::new(self.inner.devpath())
247    }
248}
249
250#[cfg(feature = "serde-serialize")]
251use serde::{Deserialize, Serialize};
252
253/// Platform specific representation of axis or button.
254#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
255#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
256#[repr(transparent)]
257pub struct EvCode(platform::EvCode);
258
259impl EvCode {
260    /// Return platform-specific event code packed into a single `u32`.
261    pub fn into_u32(self) -> u32 {
262        self.0.into_u32()
263    }
264}
265
266impl Display for EvCode {
267    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
268        self.0.fmt(f)
269    }
270}
271
272/// Error type which can be returned when creating `Gilrs`.
273///
274/// Private version of `Error` that use `platform::Gilrs`.
275#[derive(Debug)]
276enum PlatformError {
277    /// Gilrs does not support the current platform, but you can use dummy context from this error if
278    /// gamepad input is not essential.
279    #[allow(dead_code)]
280    NotImplemented(platform::Gilrs),
281    /// Platform specific error.
282    #[allow(dead_code)]
283    Other(Box<dyn error::Error + Send + Sync>),
284}
285
286impl Display for PlatformError {
287    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
288        match *self {
289            PlatformError::NotImplemented(_) => {
290                f.write_str("Gilrs does not support current platform.")
291            }
292            PlatformError::Other(ref e) => e.fmt(f),
293        }
294    }
295}
296
297impl error::Error for PlatformError {
298    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
299        match self {
300            PlatformError::Other(e) => Some(e.as_ref()),
301            _ => None,
302        }
303    }
304}
305
306/// Error type which can be returned when creating `Gilrs`.
307#[non_exhaustive]
308#[derive(Debug)]
309pub enum Error {
310    /// Gilrs does not support current platform, but you can use dummy context from this error if
311    /// gamepad input is not essential.
312    NotImplemented(Gilrs),
313    /// Platform specific error.
314    Other(Box<dyn error::Error + Send + Sync + 'static>),
315}
316
317impl Display for Error {
318    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
319        match *self {
320            Error::NotImplemented(_) => f.write_str("Gilrs does not support current platform."),
321            Error::Other(ref e) => e.fmt(f),
322        }
323    }
324}
325
326impl error::Error for Error {
327    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
328        match self {
329            Error::Other(e) => Some(e.as_ref()),
330            _ => None,
331        }
332    }
333}
334
335/// Provides the most common mappings of physical location of gamepad elements to their EvCodes.
336/// Some (or most) gamepads may use different mappings.
337pub mod native_ev_codes {
338    use super::EvCode;
339    use crate::platform::native_ev_codes as nec;
340
341    pub const AXIS_LSTICKX: EvCode = EvCode(nec::AXIS_LSTICKX);
342    pub const AXIS_LSTICKY: EvCode = EvCode(nec::AXIS_LSTICKY);
343    pub const AXIS_LEFTZ: EvCode = EvCode(nec::AXIS_LEFTZ);
344    pub const AXIS_RSTICKX: EvCode = EvCode(nec::AXIS_RSTICKX);
345    pub const AXIS_RSTICKY: EvCode = EvCode(nec::AXIS_RSTICKY);
346    pub const AXIS_RIGHTZ: EvCode = EvCode(nec::AXIS_RIGHTZ);
347    pub const AXIS_DPADX: EvCode = EvCode(nec::AXIS_DPADX);
348    pub const AXIS_DPADY: EvCode = EvCode(nec::AXIS_DPADY);
349    pub const AXIS_RT: EvCode = EvCode(nec::AXIS_RT);
350    pub const AXIS_LT: EvCode = EvCode(nec::AXIS_LT);
351    pub const AXIS_RT2: EvCode = EvCode(nec::AXIS_RT2);
352    pub const AXIS_LT2: EvCode = EvCode(nec::AXIS_LT2);
353
354    pub const BTN_SOUTH: EvCode = EvCode(nec::BTN_SOUTH);
355    pub const BTN_EAST: EvCode = EvCode(nec::BTN_EAST);
356    pub const BTN_C: EvCode = EvCode(nec::BTN_C);
357    pub const BTN_NORTH: EvCode = EvCode(nec::BTN_NORTH);
358    pub const BTN_WEST: EvCode = EvCode(nec::BTN_WEST);
359    pub const BTN_Z: EvCode = EvCode(nec::BTN_Z);
360    pub const BTN_LT: EvCode = EvCode(nec::BTN_LT);
361    pub const BTN_RT: EvCode = EvCode(nec::BTN_RT);
362    pub const BTN_LT2: EvCode = EvCode(nec::BTN_LT2);
363    pub const BTN_RT2: EvCode = EvCode(nec::BTN_RT2);
364    pub const BTN_SELECT: EvCode = EvCode(nec::BTN_SELECT);
365    pub const BTN_START: EvCode = EvCode(nec::BTN_START);
366    pub const BTN_MODE: EvCode = EvCode(nec::BTN_MODE);
367    pub const BTN_LTHUMB: EvCode = EvCode(nec::BTN_LTHUMB);
368    pub const BTN_RTHUMB: EvCode = EvCode(nec::BTN_RTHUMB);
369
370    pub const BTN_DPAD_UP: EvCode = EvCode(nec::BTN_DPAD_UP);
371    pub const BTN_DPAD_DOWN: EvCode = EvCode(nec::BTN_DPAD_DOWN);
372    pub const BTN_DPAD_LEFT: EvCode = EvCode(nec::BTN_DPAD_LEFT);
373    pub const BTN_DPAD_RIGHT: EvCode = EvCode(nec::BTN_DPAD_RIGHT);
374}