ferroui_linuxframebuffer/input/ev_dev/
ev_dev_backend.rs1use super::ev_dev_touch_screen::handle_events;
10use super::{
11 EvDevDevice, EvDevDeviceDescription, EvDevDeviceHandler, EvDevKeyboard, EvDevMouse, EvDevMultiTouchScreen,
12 EvDevRawEvent, EvDevSingleTouchScreen,
13};
14use crate::input::{IInputBackend, IScreenInfoProvider};
15use crate::native_unsafe_methods::input_event;
16use crate::raw_event_grouping::RawEvent;
17use ferroui_base::input::raw::{
18 RawKeyEventArgs, RawMouseWheelEventArgs, RawPointerEventArgs, RawTextInputEventArgs, RawTouchEventArgs,
19};
20use ferroui_base::input::{IInputDevice, IInputRoot, KeyDeviceType, MouseDevice, TouchDevice};
21use ferroui_base::threading::{Dispatcher, DispatcherPriority};
22use std::cell::{Cell, RefCell};
23use std::collections::HashMap;
24use std::os::fd::RawFd;
25use std::rc::{Rc, Weak};
26use std::sync::atomic::{AtomicU64, Ordering};
27use std::sync::Arc;
28use std::thread;
29
30const DEVICE_VARIABLE_PREFIX: &str = "FERROUI_EVDEV_DEVICE_";
32
33struct DeviceEntry {
34 _device: EvDevDevice,
36 handler: Box<dyn EvDevDeviceHandler>,
37 dropped: bool,
39 pointer_device: Rc<dyn IInputDevice>,
41}
42
43#[derive(Default)]
44struct State {
45 handlers: RefCell<Vec<DeviceEntry>>,
46 on_input: RefCell<Option<Rc<dyn Fn(RawEvent)>>>,
47 input_root: RefCell<Option<Rc<dyn IInputRoot>>>,
48 screen: RefCell<Option<Rc<dyn IScreenInfoProvider>>>,
49 warned_about_the_input_root: Cell<bool>,
50}
51
52ferroui_base::ferro_thread_local! {
53 static BACKENDS: RefCell<HashMap<u64, Weak<State>>> = RefCell::new(HashMap::new());
56}
57
58static NEXT_BACKEND: AtomicU64 = AtomicU64::new(1);
59
60pub struct EvDevBackend {
61 device_descriptions: Vec<EvDevDeviceDescription>,
62 state: Rc<State>,
63}
64
65impl State {
66 fn handle(&self, index: usize, events: &[input_event]) {
68 let Some(screen) = self.screen.borrow().clone() else {
69 return;
70 };
71 let mut out = Vec::new();
72 let pointer_device = {
73 let mut handlers = self.handlers.borrow_mut();
74 let Some(entry) = handlers.get_mut(index) else {
75 return;
76 };
77 handle_events(entry.handler.as_mut(), &mut entry.dropped, events, screen.scaled_size(), &mut out);
78 entry.pointer_device.clone()
79 };
80 if out.is_empty() {
81 return;
82 }
83
84 let Some(input_root) = self.input_root.borrow().clone() else {
85 if !self.warned_about_the_input_root.replace(true) {
88 eprintln!("InputRoot hasn't been set");
89 }
90 return;
91 };
92 let Some(on_input) = self.on_input.borrow().clone() else {
93 return;
94 };
95
96 for event in out {
97 let args: RawEvent = match event {
98 EvDevRawEvent::Touch { type_, timestamp, position, raw_pointer_id } => Rc::new(RawTouchEventArgs::new(
99 pointer_device.clone(),
100 timestamp,
101 input_root.clone(),
102 type_,
103 position,
104 ferroui_base::input::RawInputModifiers::empty(),
105 raw_pointer_id,
106 )),
107 EvDevRawEvent::Pointer { type_, timestamp, position, modifiers } => Rc::new(RawPointerEventArgs::new(
108 pointer_device.clone(),
109 timestamp,
110 input_root.clone(),
111 type_,
112 position,
113 modifiers,
114 )),
115 EvDevRawEvent::Wheel { timestamp, position, delta, modifiers } => Rc::new(RawMouseWheelEventArgs::new(
116 pointer_device.clone(),
117 timestamp,
118 input_root.clone(),
119 position,
120 delta,
121 modifiers,
122 )),
123 EvDevRawEvent::Key { type_, timestamp, key, modifiers, physical_key, key_symbol } => {
124 let Some(keyboard) = keyboard_device() else {
125 continue;
126 };
127 Rc::new(RawKeyEventArgs::new(
128 keyboard,
129 timestamp,
130 input_root.clone(),
131 type_,
132 key,
133 modifiers,
134 physical_key,
135 key_symbol,
136 KeyDeviceType::Keyboard,
137 ))
138 }
139 EvDevRawEvent::Text { timestamp, text } => {
140 let Some(keyboard) = keyboard_device() else {
141 continue;
142 };
143 Rc::new(RawTextInputEventArgs::new(keyboard, timestamp, input_root.clone(), text))
144 }
145 };
146 on_input(args);
147 }
148 }
149}
150
151fn keyboard_device() -> Option<Rc<dyn IInputDevice>> {
153 crate::LinuxFramebufferPlatform::keyboard_device().map(|keyboard| keyboard as Rc<dyn IInputDevice>)
154}
155
156fn input_thread(epoll: RawFd, fds: Vec<RawFd>, backend: u64, dispatcher: Arc<Dispatcher>) {
158 const MAX_EVENTS: usize = 16;
159 let mut events = [libc::epoll_event { events: 0, u64: 0 }; MAX_EVENTS];
160 loop {
161 let event_count = unsafe { libc::epoll_wait(epoll, events.as_mut_ptr(), MAX_EVENTS as i32, 1000) };
163 let event_count = event_count.clamp(0, MAX_EVENTS as i32) as usize;
164 for ev in &events[..event_count] {
165 let index = ev.u64 as usize;
166 let Some(&fd) = fds.get(index) else {
167 continue;
168 };
169 let read = EvDevDevice::read_events(fd);
170 if read.is_empty() {
171 continue;
172 }
173 dispatcher.post(
174 move || {
175 let state = BACKENDS.with(|backends| backends.borrow().get(&backend).and_then(Weak::upgrade));
176 if let Some(state) = state {
177 state.handle(index, &read);
178 }
179 },
180 DispatcherPriority::SEND,
181 );
182 }
183 }
184}
185
186impl EvDevBackend {
187 pub fn new(devices: Vec<EvDevDeviceDescription>) -> Self {
188 Self { device_descriptions: devices, state: Rc::new(State::default()) }
189 }
190
191 pub fn create_from_environment() -> Result<Self, String> {
195 let mut variables: Vec<(String, String)> =
196 std::env::vars().filter(|(key, _)| key.starts_with(DEVICE_VARIABLE_PREFIX)).collect();
197 variables.sort();
198
199 let mut device_descriptions = Vec::new();
200 for (_, value) in variables {
201 device_descriptions.push(EvDevDeviceDescription::parse_from_env(&value)?);
202 }
203
204 if device_descriptions.is_empty() {
205 return Err(format!(
206 "No device device description found, specify devices by adding {DEVICE_VARIABLE_PREFIX}{{name}} environment variables"
207 ));
208 }
209
210 Ok(Self::new(device_descriptions))
211 }
212}
213
214impl IInputBackend for EvDevBackend {
215 fn initialize(&self, info: Rc<dyn IScreenInfoProvider>, on_input: Rc<dyn Fn(RawEvent)>) {
219 *self.state.on_input.borrow_mut() = Some(on_input);
220 *self.state.screen.borrow_mut() = Some(info);
221 let epoll = unsafe { libc::epoll_create1(0) };
223 let mut fds = Vec::new();
224
225 for (c, description) in self.device_descriptions.iter().enumerate() {
226 let dev =
227 EvDevDevice::open(description.path().unwrap_or_default()).unwrap_or_else(|error| panic!("{error}"));
228 let failed = |error: String| -> ! { panic!("{error}: {}", description.path().unwrap_or_default()) };
229 let (handler, pointer_device): (Box<dyn EvDevDeviceHandler>, Rc<dyn IInputDevice>) = match description {
230 EvDevDeviceDescription::TouchScreen(touch) => (
231 Box::new(EvDevSingleTouchScreen::new(dev.info(), touch).unwrap_or_else(|error| failed(error))),
232 TouchDevice::new(),
233 ),
234 EvDevDeviceDescription::MultiTouchScreen(touch) => (
235 Box::new(EvDevMultiTouchScreen::new(dev.info(), touch).unwrap_or_else(|error| failed(error))),
236 TouchDevice::new(),
237 ),
238 EvDevDeviceDescription::Keyboard(_) => (Box::new(EvDevKeyboard::new()), MouseDevice::new()),
239 EvDevDeviceDescription::Mouse(_) => (Box::new(EvDevMouse::new(dev.info())), MouseDevice::new()),
240 };
241
242 let fd = dev.fd();
243 fds.push(fd);
244 self.state.handlers.borrow_mut().push(DeviceEntry {
245 _device: dev,
246 handler,
247 dropped: false,
248 pointer_device,
249 });
250
251 let mut ev = libc::epoll_event { events: libc::EPOLLIN as u32, u64: c as u64 };
252 unsafe {
254 libc::epoll_ctl(epoll, libc::EPOLL_CTL_ADD, fd, &mut ev);
255 }
256 }
257
258 let backend = NEXT_BACKEND.fetch_add(1, Ordering::Relaxed);
259 BACKENDS.with(|backends| backends.borrow_mut().insert(backend, Rc::downgrade(&self.state)));
260 let dispatcher = Dispatcher::ui_thread();
261 thread::Builder::new()
262 .name("evdev input".to_owned())
263 .spawn(move || input_thread(epoll, fds, backend, dispatcher))
264 .unwrap_or_else(|error| panic!("Unable to start the input thread: {error}"));
265 }
266
267 fn set_input_root(&self, root: Rc<dyn IInputRoot>) {
268 *self.state.input_root.borrow_mut() = Some(root);
269 }
270}