kanata_state_machine/oskbd/
linux.rs

1//! Contains the input/output code for keyboards on Linux.
2
3#![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    /// Some(_) if devices are explicitly listed, otherwise None.
34    missing_device_paths: Option<Vec<String>>,
35    poll: Poll,
36    events: Events,
37    token_counter: usize,
38    /// stored to prevent dropping
39    _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        // NOTE: This grab-ungrab-grab sequence magically fixes an issue with a Lenovo Yoga
126        // trackpad not working. No idea why this works.
127        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                        // Currently the kind() is uncategorized... not helpful, need to match
162                        // on os error. code 19 is ENODEV, "no such device".
163                        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        // This function is kinda ugly but the borrow checker doesn't like all this mutation.
199        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                        // try a few times with waits in between; device might not be ready
211                        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    // The next one is power button. Some keyboards have it,
315    // but so does the power button...
316    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        // Support pretty much every feature of a Keyboard or a Mouse in a VirtualDevice so that no event from the original input devices gets lost
388        // TODO investigate the rare possibility that a device is e.g. a Joystick and a Keyboard or a Mouse at the same time, which could lead to lost events
389
390        // For some reason 0..0x300 (max value for a key) doesn't work, the closest that I've got to work is 560
391        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            // libinput's "disable while typing" feature don't work when bus_type
410            // is set to BUS_USB, but appears to work when it's set to BUS_I8042.
411            .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() // Expect only one. Using fold or calling next again blocks indefinitely
423            .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            // historically was the only option, so make Enter the default
441            unicode_termination: Cell::new(UnicodeTermination::Enter),
442
443            // historically was the only option, so make KEY_U the default
444            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            // Possible codes are:
459            //
460            // SYN_REPORT: probably the only one we'll ever receive, segments atomic reads
461            // SYN_CONFIG: unused
462            // SYN_MT_REPORT: same as SYN_REPORT above but for touch devices, which kanata almost
463            //     certainly shouldn't be dealing with.
464            // SYN_DROPPED: buffer full, events dropped. Not sure what this means or how to handle
465            //     this correctly.
466            //
467            // With this knowledge, seems fine to not bother checking.
468            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    /// Send using C-S-u + <unicode hex number> + spc
517    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// Note for allow: the ioctl_read_buf triggers this clippy lint.
781// Note: CI does not yet support this lint, so also allowing unknown lints.
782#[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}