sweetacid_evdev/
uinput.rs1use crate::constants::EventType;
6use crate::inputid::{BusType, InputId};
7use crate::{
8 sys, AbsoluteAxisType, AttributeSetRef, InputEvent, Key, RelativeAxisType, SwitchType,
9};
10use libc::O_NONBLOCK;
11use libc::{__u16, input_absinfo, uinput_abs_setup};
12use std::fs::{File, OpenOptions};
13use std::io::{self, Write};
14use std::os::unix::{fs::OpenOptionsExt, io::AsRawFd};
15
16const UINPUT_PATH: &str = "/dev/uinput";
17
18#[derive(Debug)]
19pub struct VirtualDeviceBuilder<'a> {
20 file: File,
21 name: &'a [u8],
22 id: Option<libc::input_id>,
23}
24
25impl<'a> VirtualDeviceBuilder<'a> {
26 pub fn new() -> io::Result<Self> {
27 let mut options = OpenOptions::new();
28
29 let file = options
31 .write(true)
32 .custom_flags(O_NONBLOCK)
33 .open(UINPUT_PATH)?;
34
35 Ok(VirtualDeviceBuilder {
36 file,
37 name: Default::default(),
38 id: None,
39 })
40 }
41
42 #[inline]
43 pub fn name<S: AsRef<[u8]> + ?Sized>(mut self, name: &'a S) -> Self {
44 self.name = name.as_ref();
45 self
46 }
47
48 #[inline]
49 pub fn input_id(mut self, id: InputId) -> Self {
50 self.id = Some(id.0);
51 self
52 }
53
54 pub fn with_keys(self, keys: &AttributeSetRef<Key>) -> io::Result<Self> {
55 unsafe {
57 sys::ui_set_evbit(
58 self.file.as_raw_fd(),
59 crate::EventType::KEY.0 as nix::sys::ioctl::ioctl_param_type,
60 )?;
61 }
62
63 for bit in keys.iter() {
64 unsafe {
65 sys::ui_set_keybit(
66 self.file.as_raw_fd(),
67 bit.0 as nix::sys::ioctl::ioctl_param_type,
68 )?;
69 }
70 }
71
72 Ok(self)
73 }
74
75 pub fn with_absolute_axes(self, axes: &AttributeSetRef<AbsoluteAxisType>) -> io::Result<Self> {
76 unsafe {
77 sys::ui_set_evbit(
78 self.file.as_raw_fd(),
79 crate::EventType::ABSOLUTE.0 as nix::sys::ioctl::ioctl_param_type,
80 )?;
81 }
82
83 for bit in axes.iter() {
84 println!("bit: {:?}", bit);
85 println!("bit.0: {}", bit.0);
86
87 unsafe {
88 sys::ui_set_absbit(
89 self.file.as_raw_fd(),
90 bit.0 as nix::sys::ioctl::ioctl_param_type,
91 )?;
92
93 sys::ui_abs_setup(
94 self.file.as_raw_fd(),
95 &uinput_abs_setup {
96 code: bit.0 as __u16,
97 absinfo: input_absinfo {
98 flat: 0,
99 fuzz: 0,
100 maximum: 255,
101 minimum: 0,
102 resolution: 255,
103 value: 0,
104 },
105 },
106 )?;
107 }
108 }
109
110 Ok(self)
111 }
112
113 pub fn with_relative_axes(self, axes: &AttributeSetRef<RelativeAxisType>) -> io::Result<Self> {
114 unsafe {
115 sys::ui_set_evbit(
116 self.file.as_raw_fd(),
117 crate::EventType::RELATIVE.0 as nix::sys::ioctl::ioctl_param_type,
118 )?;
119 }
120
121 for bit in axes.iter() {
122 unsafe {
123 sys::ui_set_relbit(
124 self.file.as_raw_fd(),
125 bit.0 as nix::sys::ioctl::ioctl_param_type,
126 )?;
127 }
128 }
129
130 Ok(self)
131 }
132
133 pub fn with_switches(self, switches: &AttributeSetRef<SwitchType>) -> io::Result<Self> {
134 unsafe {
135 sys::ui_set_evbit(
136 self.file.as_raw_fd(),
137 crate::EventType::SWITCH.0 as nix::sys::ioctl::ioctl_param_type,
138 )?;
139 }
140
141 for bit in switches.iter() {
142 unsafe {
143 sys::ui_set_swbit(
144 self.file.as_raw_fd(),
145 bit.0 as nix::sys::ioctl::ioctl_param_type,
146 )?;
147 }
148 }
149
150 Ok(self)
151 }
152
153 pub fn build(self) -> io::Result<VirtualDevice> {
154 let mut usetup = libc::uinput_setup {
157 id: self.id.unwrap_or(DEFAULT_ID),
158 name: [0; libc::UINPUT_MAX_NAME_SIZE],
159 ff_effects_max: 0,
160 };
161
162 let name_bytes = unsafe { &*(self.name as *const [u8] as *const [libc::c_char]) };
164 assert!(name_bytes.len() + 1 < libc::UINPUT_MAX_NAME_SIZE);
168 usetup.name[..name_bytes.len()].copy_from_slice(name_bytes);
169
170 VirtualDevice::new(self.file, &usetup)
171 }
172}
173
174const DEFAULT_ID: libc::input_id = libc::input_id {
175 bustype: BusType::BUS_USB.0,
176 vendor: 0x1234, product: 0x5678, version: 0x111,
179};
180
181pub struct VirtualDevice {
182 file: File,
183}
184
185impl VirtualDevice {
186 fn new(file: File, usetup: &libc::uinput_setup) -> io::Result<Self> {
188 unsafe { sys::ui_dev_setup(file.as_raw_fd(), usetup)? };
189 unsafe { sys::ui_dev_create(file.as_raw_fd())? };
190
191 Ok(VirtualDevice { file })
192 }
193
194 #[inline]
195 fn write_raw(&mut self, messages: &[InputEvent]) -> io::Result<()> {
196 let bytes = unsafe { crate::cast_to_bytes(messages) };
197 self.file.write_all(bytes)
198 }
199
200 pub fn emit(&mut self, messages: &[InputEvent]) -> io::Result<()> {
205 self.write_raw(messages)?;
206
207 let syn = InputEvent::new(EventType::SYNCHRONIZATION, 0, 0);
209 self.write_raw(&[syn])?;
210
211 Ok(())
212 }
213}