touch_event/
lib.rs

1pub(crate) mod analyze;
2mod read;
3pub(crate) mod touch_group;
4
5use std::{
6    collections::HashMap,
7    error::Error,
8    fs,
9    ops::Deref,
10    sync::{
11        atomic::AtomicUsize,
12        mpsc::{self, Receiver},
13        Arc,
14    },
15    thread,
16    time::Duration,
17};
18
19use atomic::{Atomic, Ordering};
20use evdev::{Device, EventType};
21
22/// Listen for touch events
23///
24/// Implemented[`std::ops::Deref`]to access internal`status_map`
25///
26/// # Example
27///
28/// ```ignore
29/// use std::{thread, time::Duration, sync::atomic::Ordering};
30/// use touch_event::TouchListener;
31///
32/// let listener = TouchListener::new(5).unwrap();
33/// thread::sleep(Duration::from_secs(1)); // Just listen for a while
34///
35/// // Deref to HashMap inside it
36/// for atom_status in listener.values() {
37///     let status = atom_status.load(Ordering::Acquire);
38///     println!("{status:?}");
39/// }
40/// ```
41///
42/// note: This is untested when I was documenting, because my compiling environment does not have a touch screen and cannot test this.But it has been tested on other devices
43
44#[derive(Debug)]
45pub struct TouchListener {
46    status_map: HashMap<usize, Arc<AtomicTouchStatus>>,
47    wait: Receiver<()>,
48    min_pixel: Arc<AtomicUsize>,
49}
50
51pub(crate) type AtomicTouchStatus = Atomic<TouchStatus>;
52
53/// Indicates the current touch state
54///
55/// [`TouchStatus::Slide`]: At least one touch point is sliding. Of course, this also means that there is at least one touch point on the screen
56///
57/// [`TouchStatus::Click`]: There are touch points on the screen, but no touch points are sliding
58///
59/// [`TouchStatus::None`]: There are no touch points
60#[derive(Debug, Clone, Copy, Eq, PartialEq)]
61pub enum TouchStatus {
62    Slide,
63    Click,
64    None,
65}
66
67impl Deref for TouchListener {
68    type Target = HashMap<usize, Arc<AtomicTouchStatus>>;
69
70    fn deref(&self) -> &Self::Target {
71        &self.status_map
72    }
73}
74
75impl TouchListener {
76    /// Allocate a listening thread for each touch device, and construct [`TouchListener`]
77    ///
78    /// min_pixel: the minimum pixel point judged as sliding, recommand 5
79    ///
80    /// # Errors
81    ///
82    /// No touch device
83    ///
84    /// Failed to create thread
85    pub fn new(min_pixel: usize) -> Result<Self, Box<dyn Error>> {
86        let devices: Vec<_> = fs::read_dir("/dev/input")?
87            .filter_map(|f| {
88                let f = f.ok()?;
89                let path = f.path();
90                let device = Device::open(path).ok()?;
91
92                let event_len = "event".len();
93                let id: usize = f.file_name().into_string().ok()?[event_len..]
94                    .trim()
95                    .parse()
96                    .ok()?;
97
98                Some((id, device))
99            })
100            .filter(|(_, d)| d.supported_events().contains(EventType::ABSOLUTE))
101            .collect();
102
103        if devices.is_empty() {
104            return Err("No usable touch device".into());
105        }
106
107        let mut status_map = HashMap::new();
108        let (sx, rx) = mpsc::sync_channel(1);
109        let min_pixel = Arc::new(AtomicUsize::new(min_pixel));
110
111        for (id, device) in devices {
112            let touch_status = Arc::new(Atomic::new(TouchStatus::None));
113            let touch_status_clone = touch_status.clone();
114            let sx = sx.clone();
115            let min_pixel = min_pixel.clone();
116
117            status_map.insert(id, touch_status);
118
119            thread::Builder::new()
120                .name("TouchDeviceListener".into())
121                .spawn(move || read::daemon_thread(device, &touch_status_clone, &sx, &min_pixel))?;
122        }
123
124        if status_map.is_empty() {
125            return Err("No usable touch device".into());
126        }
127
128        Ok(Self {
129            status_map,
130            wait: rx,
131            min_pixel,
132        })
133    }
134
135    /// Set the minimum pixel point judged as sliding
136    pub fn min_pixel(&self, p: usize) {
137        self.min_pixel.store(p, Ordering::Release);
138    }
139
140    /// Block and waiting for touch status to update
141    ///
142    /// # Errors
143    ///
144    /// Monitor threads had paniced
145    #[inline]
146    pub fn wait(&self) -> Result<(), &'static str> {
147        self.wait.recv().map_err(|_| "Monitor threads had paniced")
148    }
149
150    /// Block and waiting for touch status to update, unless timeout
151    ///
152    /// # Errors
153    ///
154    /// Monitor threads had paniced
155    #[inline]
156    pub fn wait_timeout(&self, t: Duration) -> Result<(), &'static str> {
157        self.wait
158            .recv_timeout(t)
159            .map_err(|_| "Monitor threads had paniced")
160    }
161
162    /// Analyze the status of all current devices and return a comprehensive status.
163    ///
164    /// [`TouchStatus::Slide`] state, [`TouchStatus::Click`] state, [`TouchStatus::None`] state
165    ///
166    /// If at least one device is in the corresponding state, then the corresponding state is true
167    pub fn status(&self) -> (bool, bool, bool) {
168        let slide = self
169            .status_map
170            .values()
171            .any(|s| s.load(Ordering::Acquire) == TouchStatus::Slide);
172        let click = self
173            .status_map
174            .values()
175            .any(|s| s.load(Ordering::Acquire) == TouchStatus::Click);
176        let none = self
177            .status_map
178            .values()
179            .any(|s| s.load(Ordering::Acquire) == TouchStatus::None);
180
181        (slide, click, none)
182    }
183}