smithay_drm_extras/drm_scanner/
connector_scanner.rs

1use std::{
2    collections::HashMap,
3    iter::{Chain, Map},
4};
5
6use drm::control::{connector, Device as ControlDevice};
7
8/// Responsible for tracking connected/disconnected events.
9///
10/// ### Example
11/// ```no_run
12/// # mod helpers { include!("../docs/doctest_helpers.rs"); };
13/// # let drm_device: helpers::FakeDevice = todo!();
14/// use smithay_drm_extras::drm_scanner::{ConnectorScanner, ConnectorScanEvent};
15///
16/// let mut scanner = ConnectorScanner::new();
17///
18/// for event in scanner.scan(&drm_device) {
19///     match event {
20///         ConnectorScanEvent::Connected(conn) => {},
21///         ConnectorScanEvent::Disconnected(conn) => {},
22///     }
23/// }
24#[derive(Debug, Default)]
25pub struct ConnectorScanner {
26    connectors: HashMap<connector::Handle, connector::Info>,
27}
28
29impl ConnectorScanner {
30    /// Create new [`ConnectorScanner`]
31    pub fn new() -> Self {
32        Default::default()
33    }
34
35    /// Should be called on every device changed event
36    pub fn scan(&mut self, drm: &impl ControlDevice) -> std::io::Result<ConnectorScanResult> {
37        let res_handles = drm.resource_handles()?;
38        let connector_handles = res_handles.connectors();
39
40        let mut added = Vec::new();
41        let mut removed = Vec::new();
42
43        for conn in connector_handles
44            .iter()
45            .filter_map(|conn| drm.get_connector(*conn, true).ok())
46        {
47            let curr_state = conn.state();
48
49            use connector::State;
50            if let Some(old) = self.connectors.insert(conn.handle(), conn.clone()) {
51                match (old.state(), curr_state) {
52                    (State::Connected, State::Disconnected) => removed.push(conn),
53                    (State::Disconnected | State::Unknown, State::Connected) => added.push(conn),
54                    //
55                    (State::Connected, State::Connected) => {}
56                    (State::Disconnected, State::Disconnected) => {}
57                    //
58                    (State::Unknown, _) => {}
59                    (_, State::Unknown) => {}
60                }
61            } else if curr_state == State::Connected {
62                added.push(conn)
63            }
64        }
65
66        Ok(ConnectorScanResult {
67            connected: added,
68            disconnected: removed,
69        })
70    }
71
72    /// Get map of all connectors, connected and disconnected ones.
73    pub fn connectors(&self) -> &HashMap<connector::Handle, connector::Info> {
74        &self.connectors
75    }
76}
77
78/// Result of [`ConnectorScanner::scan`]
79///
80/// You can use `added` and `removed` fields of this result manually,
81/// or you can just iterate (using [`IntoIterator`] or [`ConnectorScanResult::iter`])
82/// over this result to get [`ConnectorScanEvent`].
83#[derive(Debug, Default, Clone)]
84pub struct ConnectorScanResult {
85    /// Connectors that got plugged in since last scan
86    pub connected: Vec<connector::Info>,
87    /// Connectors that got unplugged since last scan
88    pub disconnected: Vec<connector::Info>,
89}
90
91/// Created from [`ConnectorScanResult`], informs about connector events.
92#[derive(Debug, Clone)]
93pub enum ConnectorScanEvent {
94    /// A new connector got plugged in since last scan
95    Connected(connector::Info),
96    /// A connector got unplugged in since last scan
97    Disconnected(connector::Info),
98}
99
100impl ConnectorScanResult {
101    /// Creates event iterator for this result
102    ///
103    /// Internally this clones the data so it is equivalent to [`IntoIterator`]
104    pub fn iter(&self) -> impl Iterator<Item = ConnectorScanEvent> {
105        self.clone().into_iter()
106    }
107}
108
109type ConnectorScanItemToEvent = fn(connector::Info) -> ConnectorScanEvent;
110
111impl IntoIterator for ConnectorScanResult {
112    type Item = ConnectorScanEvent;
113    type IntoIter = Chain<
114        Map<std::vec::IntoIter<connector::Info>, ConnectorScanItemToEvent>,
115        Map<std::vec::IntoIter<connector::Info>, ConnectorScanItemToEvent>,
116    >;
117
118    fn into_iter(self) -> Self::IntoIter {
119        self.disconnected
120            .into_iter()
121            .map(ConnectorScanEvent::Disconnected as ConnectorScanItemToEvent)
122            .chain(
123                self.connected
124                    .into_iter()
125                    .map(ConnectorScanEvent::Connected as ConnectorScanItemToEvent),
126            )
127    }
128}