evdev_shortcut/
listener.rs1use crate::{DeviceOpenError, Key, Shortcut, ShortcutEvent, ShortcutState};
2use async_stream::stream;
3use evdev::Device;
4use futures::pin_mut;
5use futures::stream::iter;
6use futures::{Stream, StreamExt};
7use std::collections::HashSet;
8use std::convert::TryFrom;
9use std::path::Path;
10use std::sync::{Arc, Mutex};
11use tracing::{debug, info, trace};
12
13#[derive(Default)]
33pub struct ShortcutListener {
34 shortcuts: Arc<Mutex<HashSet<Shortcut>>>,
35}
36
37impl ShortcutListener {
38 pub fn new() -> Self {
39 ShortcutListener::default()
40 }
41
42 pub fn listen<P: AsRef<Path>>(
46 &self,
47 devices: &[P],
48 ) -> Result<impl Stream<Item = ShortcutEvent>, DeviceOpenError> {
49 let shortcuts = self.shortcuts.clone();
50
51 let devices = devices
52 .iter()
53 .map(|path| {
54 let path = path.as_ref();
55 let res = Device::open(path).map_err(|_| DeviceOpenError {
56 device: path.into(),
57 });
58 debug!(device = ?path, success = res.is_ok(), "opening input device");
59 res
60 })
61 .collect::<Result<Vec<Device>, DeviceOpenError>>()?;
62 let events = iter(
63 devices
64 .into_iter()
65 .flat_map(|device| device.into_event_stream()),
66 )
67 .flatten();
68
69 Ok(stream! {
70 let mut active_keys = HashSet::new();
71 let mut pressed_shortcuts = HashSet::new();
72
73 pin_mut!(events);
74
75 while let Some(Ok(event)) = events.next().await {
76 trace!(?event, "evdev event");
77 if let Ok(key) = Key::try_from(event.code()) {
78 match event.value() {
79 1 => active_keys.insert(key),
80 0 => active_keys.remove(&key),
81 _ => false,
82 };
83 }
84
85 let shortcuts: Vec<_> = shortcuts.lock().unwrap().iter().cloned().collect();
86
87 for shortcut in shortcuts {
88 let is_triggered = shortcut.is_triggered(&active_keys);
89 let was_triggered = pressed_shortcuts.contains(&shortcut);
90 if is_triggered && !was_triggered {
91 pressed_shortcuts.insert(shortcut.clone());
92 info!(?shortcut, "pressed");
93 yield ShortcutEvent {
94 shortcut,
95 state: ShortcutState::Pressed,
96 };
97 } else if !is_triggered && was_triggered {
98 pressed_shortcuts.remove(&shortcut);
99 info!(?shortcut, "released");
100 yield ShortcutEvent {
101 shortcut,
102 state: ShortcutState::Released,
103 };
104 }
105 }
106 }
107 })
108 }
109
110 pub fn add(&self, shortcut: Shortcut) -> bool {
112 self.shortcuts.lock().unwrap().insert(shortcut)
113 }
114
115 pub fn remove(&self, shortcut: &Shortcut) -> bool {
117 self.shortcuts.lock().unwrap().remove(shortcut)
118 }
119
120 pub fn has(&self, shortcut: &Shortcut) -> bool {
122 self.shortcuts.lock().unwrap().contains(shortcut)
123 }
124}