cognitive_device_manager/
udev.rs

1// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of
2// the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/
3
4//! Wrapper for `libudev`. Allows to find interesting devices.
5
6// -------------------------------------------------------------------------------------------------
7
8use std::ffi::OsStr;
9use std::path::Path;
10
11use libudev;
12use nix;
13
14use qualia::DeviceKind;
15
16// -------------------------------------------------------------------------------------------------
17
18const INPUT_MOUSE: &'static str = "ID_INPUT_MOUSE";
19const INPUT_TOUCHPAD: &'static str = "ID_INPUT_TOUCHPAD";
20const INPUT_KEYBOARD: &'static str = "ID_INPUT_KEYBOARD";
21
22// -------------------------------------------------------------------------------------------------
23
24/// Wrapper for `libudev`'s context.
25pub struct Udev {
26    context: libudev::Context,
27}
28
29// -------------------------------------------------------------------------------------------------
30
31impl Udev {
32    /// Constructs new "Udev".
33    pub fn new() -> Self {
34        Udev { context: libudev::Context::new().expect("Failed to create udev context") }
35    }
36
37    /// Iterates over connected input event devices and pass results to given handler.
38    /// Panics if something goes wrong.
39    pub fn iterate_input_devices<F>(&self, mut f: F)
40        where F: FnMut(&Path, DeviceKind, &libudev::Device)
41    {
42        let mut enumerator =
43            libudev::Enumerator::new(&self.context).expect("Failed to create device enumerator");
44
45        enumerator.match_subsystem("input").expect("Failed to apply filter for device enumerator");
46        for device in enumerator.scan_devices().expect("Failed to scan devices") {
47            let device_kind = determine_device_kind(&device);
48            if device_kind != DeviceKind::Unknown && is_input_device(device.sysname()) {
49                if let Some(devnode) = device.devnode() {
50                    if exists_in_filesystem(&devnode) {
51                        f(devnode, device_kind, &device);
52                    }
53                }
54            }
55        }
56    }
57
58    /// Iterates over connected output DRM devices and pass results to given handler.
59    /// Panics if something goes wrong.
60    pub fn iterate_output_devices<F: FnMut(&Path, &libudev::Device)>(&self, mut f: F) {
61        let mut enumerator =
62            libudev::Enumerator::new(&self.context).expect("Failed to create device enumerator");
63
64        enumerator.match_subsystem("drm").expect("Failed to apply filter for device enumerator");
65        for device in enumerator.scan_devices().expect("Failed to scan devices") {
66            if is_output_device(device.sysname()) {
67                if let Some(devnode) = device.devnode() {
68                    if exists_in_filesystem(&devnode) {
69                        log_info1!("Found output device: {:?}", devnode);
70                        f(devnode, &device);
71                    }
72                }
73            }
74        }
75    }
76}
77
78// -------------------------------------------------------------------------------------------------
79
80/// Checks if given device exists is event device.
81pub fn exists_in_filesystem(devnode: &Path) -> bool {
82    nix::sys::stat::stat(devnode).is_ok()
83}
84
85// -------------------------------------------------------------------------------------------------
86
87/// Checks if given sysname is for input device.
88pub fn is_input_device(sysname: &OsStr) -> bool {
89    match sysname.to_os_string().into_string() {
90        Ok(sysname) => sysname.starts_with("event"),
91        Err(_) => false,
92    }
93}
94
95// -------------------------------------------------------------------------------------------------
96
97/// Checks if given sysname is for output device.
98pub fn is_output_device(sysname: &OsStr) -> bool {
99    match sysname.to_os_string().into_string() {
100        Ok(sysname) => sysname.starts_with("card"),
101        Err(_) => false,
102    }
103}
104
105// -------------------------------------------------------------------------------------------------
106
107/// Reads devices properties and determines device kind basing on them.
108pub fn determine_device_kind(device: &libudev::Device) -> DeviceKind {
109    for property in device.properties() {
110        if property.name() == INPUT_MOUSE {
111            return DeviceKind::Mouse;
112        } else if property.name() == INPUT_TOUCHPAD {
113            return DeviceKind::Touchpad;
114        } else if property.name() == INPUT_KEYBOARD {
115            return DeviceKind::Keyboard;
116        }
117    }
118    DeviceKind::Unknown
119}
120
121// -------------------------------------------------------------------------------------------------
122