Skip to main content

joydev_rs/
device.rs

1use std::fs::{File, OpenOptions};
2use std::os::unix::prelude::*;
3use std::path::Path;
4
5use arrayref::array_ref;
6use input_event_codes::{ABS_CNT, BTN_MISC, KEY_CNT};
7
8use crate::event_codes::{AbsoluteAxis, KeyOrButton};
9use crate::io_control::{
10	get_axis_count, get_axis_mapping, get_button_count, get_button_mapping, get_correction_values, get_driver_version,
11	get_event, get_identifier, set_axis_mapping, set_button_mapping, set_correction_values,
12};
13use crate::{Correction, DeviceEvent, Result};
14
15/// Default device abstraction
16#[derive(Debug)]
17pub struct Device {
18	axis_count: u8,
19	axis_mapping: Vec<AbsoluteAxis>,
20	button_count: u8,
21	button_mapping: Vec<KeyOrButton>,
22	driver_version: u32,
23	file: File,
24	identifier: String,
25}
26
27impl Device {
28	/// Get axis count
29	pub const fn axis_count(&self) -> u8 {
30		self.axis_count
31	}
32
33	/// Get axes mapping
34	pub fn axis_mapping(&self) -> &[AbsoluteAxis; ABS_CNT as usize] {
35		array_ref!(self.axis_mapping, 0, ABS_CNT as usize)
36	}
37
38	/// Get axis mapping at index
39	pub fn axis_mapping_at(&self, number: u8) -> AbsoluteAxis {
40		self.axis_mapping[number as usize]
41	}
42
43	/// Get button count
44	pub const fn button_count(&self) -> u8 {
45		self.button_count
46	}
47
48	/// Get buttons mapping
49	pub fn button_mapping(&self) -> &[KeyOrButton; (KEY_CNT - BTN_MISC) as usize] {
50		array_ref!(self.button_mapping, 0, (KEY_CNT - BTN_MISC) as usize)
51	}
52
53	/// Get button mapping at index
54	pub fn button_mapping_at(&self, number: u8) -> KeyOrButton {
55		self.button_mapping[number as usize]
56	}
57
58	/// Get driver version
59	pub fn driver_version(&self) -> u32 {
60		self.driver_version
61	}
62
63	/// Create new device from raw `fd`
64	///
65	/// **This function expects the file to be opened at least in read mode. Other flags are optional.** Non-blocking
66	/// mode is recommended unless you really don't want it. Other flags shouldn't have any impact.
67	///
68	/// If the file is not a valid device node the will fail gracefully.
69	pub unsafe fn from_raw_fd(fd: RawFd) -> Result<Self> {
70		Self::new(File::from_raw_fd(fd))
71	}
72
73	/// Retrieve axes correction values. Wraps [`get_correction_values`](io_control/fn.get_correction_values.html).
74	pub fn get_correction_values(&self) -> Result<Vec<Correction>> {
75		get_correction_values(self.as_raw_fd())
76	}
77
78	/// Read en event. Wraps [`get_event`](io_control/fn.get_event.html).
79	pub fn get_event(&self) -> Result<DeviceEvent> {
80		Ok(DeviceEvent::from_event(self, get_event(self.as_raw_fd())?))
81	}
82
83	/// Get device identifier
84	pub fn identifier(&self) -> &str {
85		self.identifier.as_str()
86	}
87
88	/// Create new device from `file`
89	///
90	/// **This function expects the file to be opened at least in read mode. Other flags are optional.** Non-blocking
91	/// mode is recommended unless you really don't want it. Other flags shouldn't have any impact.
92	///
93	/// If the file is not a valid device node the will fail gracefully.
94	pub fn new(file: File) -> Result<Self> {
95		let axis_count = get_axis_count(file.as_raw_fd())?;
96		let axis_mapping = get_axis_mapping(file.as_raw_fd())?;
97		let button_count = get_button_count(file.as_raw_fd())?;
98		let button_mapping = get_button_mapping(file.as_raw_fd())?;
99		let driver_version = get_driver_version(file.as_raw_fd())?;
100		let identifier = get_identifier(file.as_raw_fd()).unwrap_or_else(|_| "Unknown".to_owned());
101		Ok(Self {
102			axis_count,
103			axis_mapping,
104			button_count,
105			button_mapping,
106			driver_version,
107			file,
108			identifier,
109		})
110	}
111
112	/// Create new device by opening file at `path`
113	///
114	/// **This function always tries opening the file in read-only and non-blocking mode.**
115	///
116	/// If the file is not a valid device node the will fail gracefully.
117	pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
118		Self::new(
119			OpenOptions::new()
120				.custom_flags(libc::O_NONBLOCK)
121				.read(true)
122				.open(path)?,
123		)
124	}
125
126	/// Refresh axis mapping. Wraps [`get_axis_mapping`](io_control/fn.get_axis_mapping.html).
127	pub fn refresh_axis_mapping(&mut self) -> Result<()> {
128		self.axis_mapping = get_axis_mapping(self.as_raw_fd())?;
129		Ok(())
130	}
131
132	/// Refresh button mapping. Wraps [`get_button_mapping`](io_control/fn.get_button_mapping.html).
133	pub fn refresh_button_mapping(&mut self) -> Result<()> {
134		self.button_mapping = get_button_mapping(self.as_raw_fd())?;
135		Ok(())
136	}
137
138	/// Refresh mapping for both axis and buttons
139	pub fn refresh_mapping(&mut self) -> Result<()> {
140		self.refresh_axis_mapping()?;
141		self.refresh_button_mapping()
142	}
143
144	/// Set axes mapping. Wraps [`set_axis_mapping`](io_control/fn.set_axis_mapping.html).
145	pub fn set_axis_mapping(&mut self, mapping: &[AbsoluteAxis; ABS_CNT as usize]) -> Result<()> {
146		set_axis_mapping(self.as_raw_fd(), mapping)?;
147		self.refresh_axis_mapping()
148	}
149
150	/// Set axis mapping at index. Wraps [`set_axis_mapping`](io_control/fn.set_axis_mapping.html).
151	pub fn set_axis_mapping_at(&mut self, number: u8, axis: AbsoluteAxis) -> Result<()> {
152		let mut mapping = self.axis_mapping.clone();
153		mapping[number as usize] = axis;
154		self.set_axis_mapping(array_ref!(mapping, 0, ABS_CNT as usize))
155	}
156
157	/// Set buttons mapping. Wraps [`set_button_mapping`](io_control/fn.set_button_mapping.html).
158	pub fn set_button_mapping(&mut self, mapping: &[KeyOrButton; (KEY_CNT - BTN_MISC) as usize]) -> Result<()> {
159		set_button_mapping(self.as_raw_fd(), mapping)?;
160		self.refresh_button_mapping()
161	}
162
163	/// Set button mapping at index. Wraps [`set_button_mapping`](io_control/fn.set_button_mapping.html).
164	pub fn set_button_mapping_at(&mut self, number: u8, button: KeyOrButton) -> Result<()> {
165		let mut mapping = self.button_mapping.clone();
166		mapping[number as usize] = button;
167		self.set_button_mapping(array_ref!(mapping, 0, (KEY_CNT - BTN_MISC) as usize))
168	}
169
170	/// Set axes correction values. Wraps [`set_correction_values`](io_control/fn.set_correction_values.html).
171	pub fn set_correction_values(&self, mapping: &[Correction; ABS_CNT as usize]) -> Result<()> {
172		set_correction_values(self.as_raw_fd(), mapping)
173	}
174}
175
176impl AsRawFd for Device {
177	fn as_raw_fd(&self) -> RawFd {
178		self.file.as_raw_fd()
179	}
180}
181
182impl IntoRawFd for Device {
183	fn into_raw_fd(self) -> RawFd {
184		self.file.into_raw_fd()
185	}
186}