evdevil/reader/
async.rs

1#![cfg(any(feature = "tokio", feature = "async-io"))]
2
3#[cfg(all(not(doc), feature = "tokio", feature = "async-io"))]
4compile_error!("`tokio` and `async-io` are mutually exclusive; only one may be enabled");
5
6use std::{io, os::fd::AsRawFd, task::Poll};
7
8use crate::{EventReader, event::InputEvent, reader::Report, util::r#async::AsyncHelper};
9
10/// An asynchronous iterator over [`Report`]s emitted by the device.
11///
12/// Returned by [`EventReader::async_reports`].
13///
14/// Note that this type does not yet implement the `AsyncIterator` trait, since that is still
15/// unstable.
16/// To fetch [`Report`]s, use [`AsyncReports::next_report`].
17#[derive(Debug)]
18pub struct AsyncReports<'a> {
19    helper: AsyncHelper,
20    reader: &'a mut EventReader,
21}
22
23impl<'a> AsyncReports<'a> {
24    pub(crate) fn new(reader: &'a mut EventReader) -> io::Result<Self> {
25        Ok(Self {
26            helper: AsyncHelper::new(reader.as_raw_fd())?,
27            reader,
28        })
29    }
30
31    /// Asynchronously fetches the next [`Report`] from the device.
32    ///
33    /// When using the `"tokio"` feature, this method must be called from within a tokio context.
34    pub async fn next_report(&mut self) -> io::Result<Report> {
35        self.helper
36            .asyncify(|| match self.reader.reports().next() {
37                Some(res) => Poll::Ready(res),
38                None => Poll::Pending,
39            })
40            .await
41    }
42}
43
44/// An asynchronous iterator over [`InputEvent`]s produced by an [`EventReader`].
45///
46/// Returned by [`EventReader::async_events`].
47///
48/// Note that this type does not yet implement the `AsyncIterator` trait, since that is still
49/// unstable.
50/// To fetch [`InputEvent`]s, use [`AsyncEvents::next_event`].
51#[derive(Debug)]
52pub struct AsyncEvents<'a> {
53    helper: AsyncHelper,
54    reader: &'a mut EventReader,
55}
56
57impl<'a> AsyncEvents<'a> {
58    pub(crate) fn new(reader: &'a mut EventReader) -> io::Result<Self> {
59        Ok(Self {
60            helper: AsyncHelper::new(reader.as_raw_fd())?,
61            reader,
62        })
63    }
64
65    /// Asynchronously fetches the next [`InputEvent`] from the [`EventReader`].
66    ///
67    /// When using the `"tokio"` feature, this method must be called from within a tokio context.
68    pub async fn next_event(&mut self) -> io::Result<InputEvent> {
69        self.helper
70            .asyncify(|| match self.reader.events().next() {
71                Some(res) => Poll::Ready(res),
72                None => Poll::Pending,
73            })
74            .await
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use std::io;
81
82    use crate::{
83        event::{Rel, RelEvent, Syn},
84        test::{check_events, pair},
85        util::r#async::test::AsyncTest,
86    };
87
88    #[test]
89    fn smoke() -> io::Result<()> {
90        let (uinput, evdev) = pair(|b| b.with_rel_axes([Rel::DIAL]))?;
91        let mut reader = evdev.into_reader()?;
92
93        {
94            let event = AsyncTest::new(async { reader.async_events()?.next_event().await }, || {
95                uinput.write(&[RelEvent::new(Rel::DIAL, 1).into()])
96            })
97            .run()?;
98
99            check_events([event], [RelEvent::new(Rel::DIAL, 1).into()]);
100        }
101
102        let ev = reader.events().next().unwrap()?;
103        check_events([ev], [Syn::REPORT.into()]);
104
105        let report = AsyncTest::new(
106            async { reader.async_reports()?.next_report().await },
107            || uinput.write(&[RelEvent::new(Rel::DIAL, 2).into()]),
108        )
109        .run()?;
110
111        assert_eq!(report.len(), 2);
112        check_events(
113            report,
114            [RelEvent::new(Rel::DIAL, 2).into(), Syn::REPORT.into()],
115        );
116
117        Ok(())
118    }
119}