sweetacid_evdev/
uinput.rs

1//! Virtual device emulation for evdev via uinput.
2//!
3//! This is quite useful when testing/debugging devices, or synchronization.
4
5use 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        // Open in write-only, in nonblocking mode.
30        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        // Run ioctls for setting capability bits
56        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        // Populate the uinput_setup struct
155
156        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        // SAFETY: either casting [u8] to [u8], or [u8] to [i8], which is the same size
163        let name_bytes = unsafe { &*(self.name as *const [u8] as *const [libc::c_char]) };
164        // Panic if we're doing something really stupid
165        // + 1 for the null terminator; usetup.name was zero-initialized so there will be null
166        // bytes after the part we copy into
167        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,  /* sample vendor */
177    product: 0x5678, /* sample product */
178    version: 0x111,
179};
180
181pub struct VirtualDevice {
182    file: File,
183}
184
185impl VirtualDevice {
186    /// Create a new virtual device.
187    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    /// Post a set of messages to the virtual device.
201    ///
202    /// This inserts a SYN_REPORT for you, because apparently uinput requires that for the
203    /// kernel to realize we're done.
204    pub fn emit(&mut self, messages: &[InputEvent]) -> io::Result<()> {
205        self.write_raw(messages)?;
206
207        // Now we have to write a SYN_REPORT as well.
208        let syn = InputEvent::new(EventType::SYNCHRONIZATION, 0, 0);
209        self.write_raw(&[syn])?;
210
211        Ok(())
212    }
213}