1#![cfg_attr(feature = "simulated_output", allow(dead_code, unused_imports))]
4
5pub use evdev::BusType;
6use evdev::{Device, EventType, InputEvent, KeyCode, PropType, RelativeAxisCode, uinput};
7use inotify::{Inotify, WatchMask};
8use mio::{Events, Interest, Poll, Token, unix::SourceFd};
9use nix::ioctl_read_buf;
10use rustc_hash::FxHashMap as HashMap;
11use signal_hook::{
12 consts::{SIGINT, SIGTERM, SIGTSTP},
13 iterator::Signals,
14};
15
16use std::convert::TryFrom;
17use std::fs;
18use std::io;
19use std::os::unix::io::AsRawFd;
20use std::path::PathBuf;
21use std::sync::atomic::{AtomicU64, Ordering};
22use std::thread;
23
24use super::*;
25use crate::{kanata::CalculatedMouseMove, oskbd::KeyEvent};
26use kanata_parser::cfg::DeviceDetectMode;
27use kanata_parser::cfg::UnicodeTermination;
28use kanata_parser::custom_action::*;
29use kanata_parser::keys::*;
30
31pub struct KbdIn {
32 devices: HashMap<Token, (Device, String)>,
33 missing_device_paths: Option<Vec<String>>,
35 poll: Poll,
36 events: Events,
37 token_counter: usize,
38 _inotify: Inotify,
40 include_names: Option<Vec<String>>,
41 exclude_names: Option<Vec<String>>,
42 device_detect_mode: DeviceDetectMode,
43}
44
45const INOTIFY_TOKEN_VALUE: usize = 0;
46const INOTIFY_TOKEN: Token = Token(INOTIFY_TOKEN_VALUE);
47
48pub static WAIT_DEVICE_MS: AtomicU64 = AtomicU64::new(200);
49
50impl KbdIn {
51 pub fn new(
52 dev_paths: &[String],
53 continue_if_no_devices: bool,
54 include_names: Option<Vec<String>>,
55 exclude_names: Option<Vec<String>>,
56 device_detect_mode: DeviceDetectMode,
57 ) -> Result<Self, io::Error> {
58 let poll = Poll::new()?;
59
60 let mut missing_device_paths = None;
61 let devices = if !dev_paths.is_empty() {
62 missing_device_paths = Some(vec![]);
63 devices_from_input_paths(
64 dev_paths,
65 missing_device_paths.as_mut().expect("initialized"),
66 )
67 } else {
68 discover_devices(
69 include_names.as_deref(),
70 exclude_names.as_deref(),
71 device_detect_mode,
72 )
73 };
74 if devices.is_empty() {
75 if continue_if_no_devices {
76 log::warn!("no keyboard devices found; kanata is waiting");
77 } else {
78 return Err(io::Error::new(
79 io::ErrorKind::NotFound,
80 "No keyboard devices were found. Try:\n\
81 1. Run 'kanata --list' to see available devices\n\
82 2. Check permissions: sudo usermod -a -G input $USER\n\
83 3. Log out and back in for group changes to take effect\n\
84 4. Ensure devices are connected and working",
85 ));
86 }
87 }
88 let _inotify = watch_devinput().map_err(|e| {
89 log::error!("failed to watch files: {e:?}");
90 e
91 })?;
92 poll.registry().register(
93 &mut SourceFd(&_inotify.as_raw_fd()),
94 INOTIFY_TOKEN,
95 Interest::READABLE,
96 )?;
97
98 let mut kbdin = Self {
99 poll,
100 missing_device_paths,
101 _inotify,
102 events: Events::with_capacity(32),
103 devices: HashMap::default(),
104 token_counter: INOTIFY_TOKEN_VALUE + 1,
105 include_names,
106 exclude_names,
107 device_detect_mode,
108 };
109
110 for (device, dev_path) in devices.into_iter() {
111 if let Err(e) = kbdin.register_device(device, dev_path.clone()) {
112 log::warn!("found device {dev_path} but could not register it {e:?}");
113 if let Some(ref mut missing) = kbdin.missing_device_paths {
114 missing.push(dev_path);
115 }
116 }
117 }
118
119 Ok(kbdin)
120 }
121
122 fn register_device(&mut self, mut dev: Device, path: String) -> Result<(), io::Error> {
123 log::info!("registering {path}: {:?}", dev.name().unwrap_or(""));
124 wait_for_all_keys_unpressed(&dev)?;
125 dev.grab()?;
128 dev.ungrab()?;
129 dev.grab()?;
130
131 let tok = Token(self.token_counter);
132 self.token_counter += 1;
133 let fd = dev.as_raw_fd();
134 self.poll
135 .registry()
136 .register(&mut SourceFd(&fd), tok, Interest::READABLE)?;
137 self.devices.insert(tok, (dev, path));
138 Ok(())
139 }
140
141 pub fn read(&mut self) -> Result<Vec<InputEvent>, io::Error> {
142 let mut input_events = vec![];
143 loop {
144 log::trace!("polling");
145
146 if let Err(e) = self.poll.poll(&mut self.events, None) {
147 log::error!("failed poll: {:?}", e);
148 return Ok(vec![]);
149 }
150
151 const EVENT_LIMIT: usize = 48;
152
153 let mut do_rediscover = false;
154 for event in &self.events {
155 if let Some((device, _)) = self.devices.get_mut(&event.token()) {
156 if let Err(e) = device.fetch_events().map(|evs| {
157 evs.into_iter()
158 .take(EVENT_LIMIT)
159 .for_each(|ev| input_events.push(ev))
160 }) {
161 match e.raw_os_error() {
164 Some(19) => {
165 self.poll
166 .registry()
167 .deregister(&mut SourceFd(&device.as_raw_fd()))?;
168 if let Some((_, path)) = self.devices.remove(&event.token()) {
169 log::warn!("removing kbd device: {path}");
170 if let Some(ref mut missing) = self.missing_device_paths {
171 missing.push(path);
172 }
173 }
174 }
175 _ => {
176 log::error!("failed fetch events due to {e}, kind: {}", e.kind());
177 return Err(e);
178 }
179 };
180 }
181 } else if event.token() == INOTIFY_TOKEN {
182 do_rediscover = true;
183 } else {
184 panic!("encountered unexpected epoll event {event:?}");
185 }
186 }
187 if do_rediscover {
188 log::info!("watch found file changes, looking for new devices");
189 self.rediscover_devices()?;
190 }
191 if !input_events.is_empty() {
192 return Ok(input_events);
193 }
194 }
195 }
196
197 fn rediscover_devices(&mut self) -> Result<(), io::Error> {
198 let mut paths_registered = vec![];
200 if let Some(ref mut missing) = self.missing_device_paths {
201 if missing.is_empty() {
202 log::info!("no devices are missing, doing nothing");
203 return Ok(());
204 }
205 log::info!("checking for {missing:?}");
206 let discovered_devices = missing
207 .iter()
208 .filter_map(|dev_path| {
209 for _ in 0..(WAIT_DEVICE_MS.load(Ordering::SeqCst) / 10) {
210 if let Ok(device) = Device::open(dev_path) {
212 return Some((device, dev_path.clone()));
213 }
214 std::thread::sleep(std::time::Duration::from_millis(10));
215 }
216 None
217 })
218 .collect::<Vec<(_, _)>>();
219 for (device, dev_path) in discovered_devices {
220 if let Err(e) = self.register_device(device, dev_path.clone()) {
221 log::warn!("found device {dev_path} but could not register it {e:?}");
222 } else {
223 paths_registered.push(dev_path);
224 }
225 }
226 }
227 if let Some(ref mut missing) = self.missing_device_paths {
228 missing.retain(|path| !paths_registered.contains(path));
229 } else {
230 log::info!("sleeping for a moment to let devices become ready");
231 std::thread::sleep(std::time::Duration::from_millis(
232 WAIT_DEVICE_MS.load(Ordering::SeqCst),
233 ));
234 discover_devices(
235 self.include_names.as_deref(),
236 self.exclude_names.as_deref(),
237 self.device_detect_mode,
238 )
239 .into_iter()
240 .try_for_each(|(dev, path)| {
241 if !self
242 .devices
243 .values()
244 .any(|(_, registered_path)| &path == registered_path)
245 {
246 self.register_device(dev, path)
247 } else {
248 Ok(())
249 }
250 })?;
251 }
252 Ok(())
253 }
254}
255
256#[derive(Copy, Clone, Debug, PartialEq, Eq)]
257enum DeviceType {
258 Keyboard,
259 KeyboardMouse,
260 Mouse,
261 Other,
262}
263
264pub fn is_input_device(device: &Device, detect_mode: DeviceDetectMode) -> bool {
265 if device.name() == Some("kanata") {
266 return false;
267 }
268 let is_keyboard = device.supported_keys().is_some_and(has_keyboard_keys);
269 let is_mouse = device
270 .supported_relative_axes()
271 .is_some_and(|axes| axes.contains(RelativeAxisCode::REL_X));
272 let device_type = match (is_keyboard, is_mouse) {
273 (true, true) => DeviceType::KeyboardMouse,
274 (true, false) => DeviceType::Keyboard,
275 (false, true) => DeviceType::Mouse,
276 (false, false) => DeviceType::Other,
277 };
278 let device_name = device.name().unwrap_or("unknown device name");
279 match (detect_mode, device_type) {
280 (DeviceDetectMode::Any, _)
281 | (DeviceDetectMode::KeyboardMice, DeviceType::Keyboard | DeviceType::KeyboardMouse)
282 | (DeviceDetectMode::KeyboardOnly, DeviceType::Keyboard) => {
283 let use_input = true;
284 log::debug!(
285 "Use for input autodetect: {use_input}. detect type {:?}; device type {:?}, device name: {}",
286 detect_mode,
287 device_type,
288 device_name,
289 );
290 use_input
291 }
292 (_, DeviceType::Other) => {
293 log::debug!(
294 "Use for input autodetect: false. Non-input device: {}",
295 device_name,
296 );
297 false
298 }
299 _ => {
300 let use_input = false;
301 log::debug!(
302 "Use for input autodetect: {use_input}. detect type {:?}; device type {:?}, device name: {}",
303 detect_mode,
304 device_type,
305 device_name,
306 );
307 use_input
308 }
309 }
310}
311
312fn has_keyboard_keys(keys: &evdev::AttributeSetRef<KeyCode>) -> bool {
313 const SENSIBLE_KEYBOARD_SCANCODE_LOWER_BOUND: u16 = 1;
314 const SENSIBLE_KEYBOARD_SCANCODE_UPPER_BOUND: u16 = 115;
317 let mut sensible_keyboard_keys = (SENSIBLE_KEYBOARD_SCANCODE_LOWER_BOUND
318 ..=SENSIBLE_KEYBOARD_SCANCODE_UPPER_BOUND)
319 .map(KeyCode::new);
320 sensible_keyboard_keys.any(|k| keys.contains(k))
321}
322
323impl TryFrom<InputEvent> for KeyEvent {
324 type Error = ();
325 fn try_from(item: InputEvent) -> Result<Self, Self::Error> {
326 use OsCode::*;
327 match item.destructure() {
328 evdev::EventSummary::Key(_, k, _) => Ok(Self {
329 code: OsCode::from_u16(k.0).ok_or(())?,
330 value: KeyValue::from(item.value()),
331 }),
332 evdev::EventSummary::RelativeAxis(_, axis_type, _) => {
333 let dist = item.value();
334 let code: OsCode = match axis_type {
335 RelativeAxisCode::REL_WHEEL | RelativeAxisCode::REL_WHEEL_HI_RES => {
336 if dist > 0 {
337 MouseWheelUp
338 } else {
339 MouseWheelDown
340 }
341 }
342 RelativeAxisCode::REL_HWHEEL | RelativeAxisCode::REL_HWHEEL_HI_RES => {
343 if dist > 0 {
344 MouseWheelRight
345 } else {
346 MouseWheelLeft
347 }
348 }
349 _ => return Err(()),
350 };
351 Ok(KeyEvent {
352 code,
353 value: KeyValue::Tap,
354 })
355 }
356 _ => Err(()),
357 }
358 }
359}
360
361impl From<KeyEvent> for InputEvent {
362 fn from(item: KeyEvent) -> Self {
363 InputEvent::new(EventType::KEY.0, item.code as u16, item.value as i32)
364 }
365}
366
367use std::cell::Cell;
368
369#[cfg(all(not(feature = "simulated_output"), not(feature = "passthru_ahk")))]
370pub struct KbdOut {
371 device: uinput::VirtualDevice,
372 accumulated_scroll: u16,
373 accumulated_hscroll: u16,
374 raw_buf: Vec<InputEvent>,
375 pub unicode_termination: Cell<UnicodeTermination>,
376 pub unicode_u_code: Cell<OsCode>,
377}
378
379#[cfg(all(not(feature = "simulated_output"), not(feature = "passthru_ahk")))]
380impl KbdOut {
381 pub fn new(
382 symlink_path: &Option<String>,
383 trackpoint: bool,
384 name: &str,
385 bus_type: BusType,
386 ) -> Result<Self, io::Error> {
387 let keys = evdev::AttributeSet::from_iter((0..560).map(evdev::KeyCode));
392 let relative_axes = evdev::AttributeSet::from_iter([
393 RelativeAxisCode::REL_WHEEL,
394 RelativeAxisCode::REL_HWHEEL,
395 RelativeAxisCode::REL_X,
396 RelativeAxisCode::REL_Y,
397 RelativeAxisCode::REL_Z,
398 RelativeAxisCode::REL_RX,
399 RelativeAxisCode::REL_RY,
400 RelativeAxisCode::REL_RZ,
401 RelativeAxisCode::REL_DIAL,
402 RelativeAxisCode::REL_MISC,
403 RelativeAxisCode::REL_WHEEL_HI_RES,
404 RelativeAxisCode::REL_HWHEEL_HI_RES,
405 ]);
406
407 let device = uinput::VirtualDevice::builder()?
408 .name(&name)
409 .input_id(evdev::InputId::new(bus_type, 1, 1, 1))
412 .with_keys(&keys)?
413 .with_relative_axes(&relative_axes)?;
414 let device = if trackpoint {
415 device.with_properties(&evdev::AttributeSet::from_iter([PropType::POINTING_STICK]))?
416 } else {
417 device
418 };
419 let mut device = device.build()?;
420 let devnode = device
421 .enumerate_dev_nodes_blocking()?
422 .next() .ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "devnode is not found"))??;
424 log::info!("Created device {:#?}", devnode);
425 let symlink = if let Some(symlink_path) = symlink_path {
426 let dest = PathBuf::from(symlink_path);
427 let symlink = Symlink::new(devnode, dest)?;
428 Some(symlink)
429 } else {
430 None
431 };
432 handle_signals(symlink);
433
434 Ok(KbdOut {
435 device,
436 accumulated_scroll: 0,
437 accumulated_hscroll: 0,
438 raw_buf: vec![],
439
440 unicode_termination: Cell::new(UnicodeTermination::Enter),
442
443 unicode_u_code: Cell::new(OsCode::KEY_U),
445 })
446 }
447
448 pub fn update_unicode_termination(&self, t: UnicodeTermination) {
449 self.unicode_termination.replace(t);
450 }
451
452 pub fn update_unicode_u_code(&self, u: OsCode) {
453 self.unicode_u_code.replace(u);
454 }
455
456 pub fn write_raw(&mut self, event: InputEvent) -> Result<(), io::Error> {
457 if event.event_type() == EventType::SYNCHRONIZATION {
458 self.device.emit(&self.raw_buf)?;
469 self.raw_buf.clear();
470 } else {
471 self.raw_buf.push(event);
472 }
473 Ok(())
474 }
475
476 pub fn write(&mut self, event: InputEvent) -> Result<(), io::Error> {
477 if !self.raw_buf.is_empty() {
478 self.device.emit(&self.raw_buf)?;
479 self.raw_buf.clear();
480 }
481 self.device.emit(&[event])?;
482 Ok(())
483 }
484
485 pub fn write_many(&mut self, events: &[InputEvent]) -> Result<(), io::Error> {
486 if !self.raw_buf.is_empty() {
487 self.device.emit(&self.raw_buf)?;
488 self.raw_buf.clear();
489 }
490 self.device.emit(events)?;
491 Ok(())
492 }
493
494 pub fn write_key(&mut self, key: OsCode, value: KeyValue) -> Result<(), io::Error> {
495 let key_ev = KeyEvent::new(key, value);
496 let input_ev = key_ev.into();
497 log::debug!("send to uinput: {:?}", input_ev);
498 self.device.emit(&[input_ev])?;
499 Ok(())
500 }
501
502 pub fn write_code(&mut self, code: u32, value: KeyValue) -> Result<(), io::Error> {
503 let event = InputEvent::new(EventType::KEY.0, code as u16, value as i32);
504 self.device.emit(&[event])?;
505 Ok(())
506 }
507
508 pub fn press_key(&mut self, key: OsCode) -> Result<(), io::Error> {
509 self.write_key(key, KeyValue::Press)
510 }
511
512 pub fn release_key(&mut self, key: OsCode) -> Result<(), io::Error> {
513 self.write_key(key, KeyValue::Release)
514 }
515
516 pub fn send_unicode(&mut self, c: char) -> Result<(), io::Error> {
518 log::debug!("sending unicode {c}");
519 let hex = format!("{:x}", c as u32);
520 self.press_key(OsCode::KEY_LEFTCTRL)?;
521 self.press_key(OsCode::KEY_LEFTSHIFT)?;
522 self.press_key(self.unicode_u_code.get())?;
523 self.release_key(self.unicode_u_code.get())?;
524 self.release_key(OsCode::KEY_LEFTSHIFT)?;
525 self.release_key(OsCode::KEY_LEFTCTRL)?;
526 let mut s = String::new();
527 for c in hex.chars() {
528 s.push(c);
529 let osc = str_to_oscode(&s).expect("valid keycodes for unicode");
530 s.clear();
531 self.press_key(osc)?;
532 self.release_key(osc)?;
533 }
534 match self.unicode_termination.get() {
535 UnicodeTermination::Enter => {
536 self.press_key(OsCode::KEY_ENTER)?;
537 self.release_key(OsCode::KEY_ENTER)?;
538 }
539 UnicodeTermination::Space => {
540 self.press_key(OsCode::KEY_SPACE)?;
541 self.release_key(OsCode::KEY_SPACE)?;
542 }
543 UnicodeTermination::SpaceEnter => {
544 self.press_key(OsCode::KEY_SPACE)?;
545 self.release_key(OsCode::KEY_SPACE)?;
546 self.press_key(OsCode::KEY_ENTER)?;
547 self.release_key(OsCode::KEY_ENTER)?;
548 }
549 UnicodeTermination::EnterSpace => {
550 self.press_key(OsCode::KEY_ENTER)?;
551 self.release_key(OsCode::KEY_ENTER)?;
552 self.press_key(OsCode::KEY_SPACE)?;
553 self.release_key(OsCode::KEY_SPACE)?;
554 }
555 }
556 Ok(())
557 }
558
559 pub fn click_btn(&mut self, btn: Btn) -> Result<(), io::Error> {
560 self.press_key(btn.into())
561 }
562
563 pub fn release_btn(&mut self, btn: Btn) -> Result<(), io::Error> {
564 self.release_key(btn.into())
565 }
566
567 pub fn scroll(
568 &mut self,
569 direction: MWheelDirection,
570 hi_res_distance: u16,
571 ) -> Result<(), io::Error> {
572 log::debug!("scroll: {direction:?} {hi_res_distance:?}");
573
574 let mut lo_res_distance = hi_res_distance / HI_RES_SCROLL_UNITS_IN_LO_RES;
575 let leftover_hi_res_distance = hi_res_distance % HI_RES_SCROLL_UNITS_IN_LO_RES;
576
577 match direction {
578 MWheelDirection::Up | MWheelDirection::Down => {
579 self.accumulated_scroll += leftover_hi_res_distance;
580 lo_res_distance += self.accumulated_scroll / HI_RES_SCROLL_UNITS_IN_LO_RES;
581 self.accumulated_scroll %= HI_RES_SCROLL_UNITS_IN_LO_RES;
582 }
583 MWheelDirection::Left | MWheelDirection::Right => {
584 self.accumulated_hscroll += leftover_hi_res_distance;
585 lo_res_distance += self.accumulated_hscroll / HI_RES_SCROLL_UNITS_IN_LO_RES;
586 self.accumulated_hscroll %= HI_RES_SCROLL_UNITS_IN_LO_RES;
587 }
588 }
589
590 let hi_res_scroll_event = InputEvent::new(
591 EventType::RELATIVE.0,
592 match direction {
593 MWheelDirection::Up | MWheelDirection::Down => RelativeAxisCode::REL_WHEEL_HI_RES.0,
594 MWheelDirection::Left | MWheelDirection::Right => {
595 RelativeAxisCode::REL_HWHEEL_HI_RES.0
596 }
597 },
598 match direction {
599 MWheelDirection::Up | MWheelDirection::Right => i32::from(hi_res_distance),
600 MWheelDirection::Down | MWheelDirection::Left => -i32::from(hi_res_distance),
601 },
602 );
603
604 if lo_res_distance > 0 {
605 self.write_many(&[
606 hi_res_scroll_event,
607 InputEvent::new(
608 EventType::RELATIVE.0,
609 match direction {
610 MWheelDirection::Up | MWheelDirection::Down => {
611 RelativeAxisCode::REL_WHEEL.0
612 }
613 MWheelDirection::Left | MWheelDirection::Right => {
614 RelativeAxisCode::REL_HWHEEL.0
615 }
616 },
617 match direction {
618 MWheelDirection::Up | MWheelDirection::Right => i32::from(lo_res_distance),
619 MWheelDirection::Down | MWheelDirection::Left => {
620 -i32::from(lo_res_distance)
621 }
622 },
623 ),
624 ])
625 } else {
626 self.write(hi_res_scroll_event)
627 }
628 }
629
630 pub fn move_mouse(&mut self, mv: CalculatedMouseMove) -> Result<(), io::Error> {
631 let (axis, distance) = match mv.direction {
632 MoveDirection::Up => (RelativeAxisCode::REL_Y, -i32::from(mv.distance)),
633 MoveDirection::Down => (RelativeAxisCode::REL_Y, i32::from(mv.distance)),
634 MoveDirection::Left => (RelativeAxisCode::REL_X, -i32::from(mv.distance)),
635 MoveDirection::Right => (RelativeAxisCode::REL_X, i32::from(mv.distance)),
636 };
637 self.write(InputEvent::new(EventType::RELATIVE.0, axis.0, distance))
638 }
639
640 pub fn move_mouse_many(&mut self, moves: &[CalculatedMouseMove]) -> Result<(), io::Error> {
641 let mut events = vec![];
642 for mv in moves {
643 let (axis, distance) = match mv.direction {
644 MoveDirection::Up => (RelativeAxisCode::REL_Y, -i32::from(mv.distance)),
645 MoveDirection::Down => (RelativeAxisCode::REL_Y, i32::from(mv.distance)),
646 MoveDirection::Left => (RelativeAxisCode::REL_X, -i32::from(mv.distance)),
647 MoveDirection::Right => (RelativeAxisCode::REL_X, i32::from(mv.distance)),
648 };
649 events.push(InputEvent::new(EventType::RELATIVE.0, axis.0, distance));
650 }
651 self.write_many(&events)
652 }
653
654 pub fn set_mouse(&mut self, _x: u16, _y: u16) -> Result<(), io::Error> {
655 log::warn!(
656 "setmouse does not work in Linux yet. Maybe try out warpd:\n\thttps://github.com/rvaiya/warpd"
657 );
658 Ok(())
659 }
660}
661
662fn devices_from_input_paths(
663 dev_paths: &[String],
664 missing_device_paths: &mut Vec<String>,
665) -> Vec<(Device, String)> {
666 dev_paths
667 .iter()
668 .map(|dev_path| (dev_path, Device::open(dev_path)))
669 .filter_map(|(dev_path, open_result)| match open_result {
670 Ok(d) => Some((d, dev_path.clone())),
671 Err(e) => {
672 log::warn!("failed to open device '{dev_path}': {e:?}");
673 missing_device_paths.push(dev_path.clone());
674 None
675 }
676 })
677 .collect()
678}
679
680pub fn discover_devices(
681 include_names: Option<&[String]>,
682 exclude_names: Option<&[String]>,
683 device_detect_mode: DeviceDetectMode,
684) -> Vec<(Device, String)> {
685 log::info!("looking for devices in /dev/input");
686 let devices: Vec<_> = evdev::enumerate()
687 .map(|(path, device)| {
688 (
689 device,
690 path.to_str()
691 .expect("non-utf8 path found for device")
692 .to_owned(),
693 )
694 })
695 .filter(|pd| {
696 let is_input = is_input_device(&pd.0, device_detect_mode);
697 (match include_names {
698 None => is_input,
699 Some(include_names) => {
700 let name = pd.0.name().unwrap_or("");
701 if include_names.iter().any(|include| name == include) {
702 log::info!("device [{}:{name}] is included", &pd.1);
703 true
704 } else {
705 log::info!("device [{}:{name}] is ignored", &pd.1);
706 false
707 }
708 }
709 }) && (match exclude_names {
710 None => true,
711 Some(exclude_names) => {
712 let name = pd.0.name().unwrap_or("");
713 if exclude_names.iter().any(|exclude| name == exclude) {
714 log::info!("device [{}:{name}] is excluded", &pd.1);
715 false
716 } else {
717 true
718 }
719 }
720 })
721 })
722 .collect();
723 devices
724}
725
726fn watch_devinput() -> Result<Inotify, io::Error> {
727 let inotify = Inotify::init().expect("Failed to initialize inotify");
728 inotify.watches().add("/dev/input", WatchMask::CREATE)?;
729 Ok(inotify)
730}
731
732#[derive(Clone)]
733struct Symlink {
734 dest: PathBuf,
735}
736
737impl Symlink {
738 fn new(source: PathBuf, dest: PathBuf) -> Result<Self, io::Error> {
739 if let Ok(metadata) = fs::symlink_metadata(&dest) {
740 if metadata.file_type().is_symlink() {
741 fs::remove_file(&dest)?;
742 } else {
743 return Err(io::Error::new(
744 io::ErrorKind::AlreadyExists,
745 format!(
746 "Cannot create a symlink at \"{}\": path already exists.",
747 dest.to_string_lossy()
748 ),
749 ));
750 }
751 }
752 std::os::unix::fs::symlink(&source, &dest)?;
753 log::info!("Created symlink {:#?} -> {:#?}", dest, source);
754 Ok(Self { dest })
755 }
756}
757
758fn handle_signals(symlink: Option<Symlink>) {
759 thread::spawn(|| {
760 let mut signals = Signals::new([SIGINT, SIGTERM, SIGTSTP]).expect("signals register");
761 if let Some(signal) = (&mut signals).into_iter().next() {
762 match signal {
763 SIGINT | SIGTERM => {
764 drop(symlink);
765 signal_hook::low_level::emulate_default_handler(signal)
766 .expect("run original sighandlers");
767 unreachable!();
768 }
769 SIGTSTP => {
770 drop(symlink);
771 log::warn!("got SIGTSTP, exiting instead of pausing so keyboards don't hang");
772 std::process::exit(SIGTSTP);
773 }
774 _ => unreachable!(),
775 }
776 }
777 });
778}
779
780#[allow(unknown_lints)]
783#[allow(clippy::manual_slice_size_calculation)]
784fn wait_for_all_keys_unpressed(dev: &Device) -> Result<(), io::Error> {
785 let mut pending_release = false;
786 const KEY_MAX: usize = OsCode::KEY_MAX as usize;
787 let mut keystate = [0u8; KEY_MAX / 8 + 1];
788 loop {
789 let mut n_pressed_keys = 0;
790 ioctl_read_buf!(read_keystates, 'E', 0x18, u8);
791 unsafe { read_keystates(dev.as_raw_fd(), &mut keystate) }
792 .map_err(|_| io::Error::last_os_error())?;
793 for i in 0..=KEY_MAX {
794 if (keystate[i / 8] >> (i % 8)) & 0x1 > 0 {
795 n_pressed_keys += 1;
796 }
797 }
798 match n_pressed_keys {
799 0 => break,
800 _ => pending_release = true,
801 }
802 }
803 if pending_release {
804 std::thread::sleep(std::time::Duration::from_micros(100));
805 }
806 Ok(())
807}
808
809impl Drop for Symlink {
810 fn drop(&mut self) {
811 let _ = fs::remove_file(&self.dest);
812 log::info!("Deleted symlink {:#?}", self.dest);
813 }
814}