joydev/
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, Key};
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<Key>,
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) -> &[Key; (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) -> Key {
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	///
70	/// # Safety
71	///
72	/// Safety is equivalent to that of `std::fs::File::from_raw_fd`.
73	pub unsafe fn from_raw_fd(fd: RawFd) -> Result<Self> {
74		Self::new(File::from_raw_fd(fd))
75	}
76
77	/// Retrieve axes correction values. Wraps [`get_correction_values`](io_control/fn.get_correction_values.html).
78	pub fn get_correction_values(&self) -> Result<Vec<Correction>> {
79		get_correction_values(self.as_raw_fd())
80	}
81
82	/// Read en event. Wraps [`get_event`](io_control/fn.get_event.html).
83	pub fn get_event(&self) -> Result<DeviceEvent> {
84		Ok(DeviceEvent::from_event(self, get_event(self.as_raw_fd())?))
85	}
86
87	/// Get device identifier
88	pub fn identifier(&self) -> &str {
89		self.identifier.as_str()
90	}
91
92	/// Create new device from `file`
93	///
94	/// **This function expects the file to be opened at least in read mode. Other flags are optional.** Non-blocking
95	/// mode is recommended unless you really don't want it. Other flags shouldn't have any impact.
96	///
97	/// If the file is not a valid device node the will fail gracefully.
98	pub fn new(file: File) -> Result<Self> {
99		let axis_count = get_axis_count(file.as_raw_fd())?;
100		let axis_mapping = get_axis_mapping(file.as_raw_fd())?;
101		let button_count = get_button_count(file.as_raw_fd())?;
102		let button_mapping = get_button_mapping(file.as_raw_fd())?;
103		let driver_version = get_driver_version(file.as_raw_fd())?;
104		let identifier = get_identifier(file.as_raw_fd()).unwrap_or_else(|_| "Unknown".to_owned());
105		Ok(Self {
106			axis_count,
107			axis_mapping,
108			button_count,
109			button_mapping,
110			driver_version,
111			file,
112			identifier,
113		})
114	}
115
116	/// Create new device by opening file at `path`
117	///
118	/// **This function always tries opening the file in read-only and non-blocking mode.**
119	///
120	/// If the file is not a valid device node the will fail gracefully.
121	pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
122		Self::new(
123			OpenOptions::new()
124				.custom_flags(libc::O_NONBLOCK)
125				.read(true)
126				.open(path)?,
127		)
128	}
129
130	/// Refresh axis mapping. Wraps [`get_axis_mapping`](io_control/fn.get_axis_mapping.html).
131	pub fn refresh_axis_mapping(&mut self) -> Result<()> {
132		self.axis_mapping = get_axis_mapping(self.as_raw_fd())?;
133		Ok(())
134	}
135
136	/// Refresh button mapping. Wraps [`get_button_mapping`](io_control/fn.get_button_mapping.html).
137	pub fn refresh_button_mapping(&mut self) -> Result<()> {
138		self.button_mapping = get_button_mapping(self.as_raw_fd())?;
139		Ok(())
140	}
141
142	/// Refresh mapping for both axis and buttons
143	pub fn refresh_mapping(&mut self) -> Result<()> {
144		self.refresh_axis_mapping()?;
145		self.refresh_button_mapping()
146	}
147
148	/// Set axes mapping. Wraps [`set_axis_mapping`](io_control/fn.set_axis_mapping.html).
149	pub fn set_axis_mapping(&mut self, mapping: &[AbsoluteAxis; ABS_CNT as usize]) -> Result<()> {
150		set_axis_mapping(self.as_raw_fd(), mapping)?;
151		self.refresh_axis_mapping()
152	}
153
154	/// Set axis mapping at index. Wraps [`set_axis_mapping`](io_control/fn.set_axis_mapping.html).
155	pub fn set_axis_mapping_at(&mut self, number: u8, axis: AbsoluteAxis) -> Result<()> {
156		let mut mapping = self.axis_mapping.clone();
157		mapping[number as usize] = axis;
158		self.set_axis_mapping(array_ref!(mapping, 0, ABS_CNT as usize))
159	}
160
161	/// Set buttons mapping. Wraps [`set_button_mapping`](io_control/fn.set_button_mapping.html).
162	pub fn set_button_mapping(&mut self, mapping: &[Key; (KEY_CNT - BTN_MISC) as usize]) -> Result<()> {
163		set_button_mapping(self.as_raw_fd(), mapping)?;
164		self.refresh_button_mapping()
165	}
166
167	/// Set button mapping at index. Wraps [`set_button_mapping`](io_control/fn.set_button_mapping.html).
168	pub fn set_button_mapping_at(&mut self, number: u8, button: Key) -> Result<()> {
169		let mut mapping = self.button_mapping.clone();
170		mapping[number as usize] = button;
171		self.set_button_mapping(array_ref!(mapping, 0, (KEY_CNT - BTN_MISC) as usize))
172	}
173
174	/// Set axes correction values. Wraps [`set_correction_values`](io_control/fn.set_correction_values.html).
175	pub fn set_correction_values(&self, mapping: &[Correction; ABS_CNT as usize]) -> Result<()> {
176		set_correction_values(self.as_raw_fd(), mapping)
177	}
178}
179
180impl AsRawFd for Device {
181	fn as_raw_fd(&self) -> RawFd {
182		self.file.as_raw_fd()
183	}
184}
185
186impl IntoRawFd for Device {
187	fn into_raw_fd(self) -> RawFd {
188		self.file.into_raw_fd()
189	}
190}