layer_shika_composition/
selector.rs

1use crate::{OutputHandle, OutputInfo};
2use std::fmt::{Debug, Formatter, Result as FmtResult};
3use std::sync::Arc;
4
5/// Runtime information about a surface instance
6#[derive(Debug, Clone)]
7pub struct SurfaceInfo {
8    /// Surface component name
9    pub name: String,
10    /// Handle to the output displaying this surface
11    pub output: OutputHandle,
12}
13
14/// Selector for targeting surfaces when setting up callbacks or runtime configuration
15///
16/// Combine with `.or()`, `.except()`, or `.on()` for complex targeting.
17#[derive(Clone)]
18pub enum Surface {
19    /// Select all surfaces
20    All,
21    /// Select surface by exact name
22    Named(String),
23    /// Select any surface matching one of the given names
24    Any(Vec<String>),
25    /// Select surfaces matching a custom predicate
26    Filter(Arc<dyn Fn(&SurfaceInfo) -> bool + Send + Sync>),
27    /// Invert selection
28    Not(Box<Surface>),
29    /// Union of multiple selectors
30    Or(Vec<Surface>),
31}
32
33impl Surface {
34    /// Selects all surfaces
35    pub fn all() -> Self {
36        Self::All
37    }
38
39    /// Selects a surface by exact name
40    pub fn named(name: impl Into<String>) -> Self {
41        Self::Named(name.into())
42    }
43
44    /// Selects surfaces matching any of the given names
45    pub fn any(names: impl IntoIterator<Item = impl Into<String>>) -> Self {
46        Self::Any(names.into_iter().map(Into::into).collect())
47    }
48
49    /// Selects surfaces matching a custom predicate
50    pub fn matching<F>(predicate: F) -> Self
51    where
52        F: Fn(&SurfaceInfo) -> bool + Send + Sync + 'static,
53    {
54        Self::Filter(Arc::new(predicate))
55    }
56
57    /// Combines this surface selector with an output selector
58    pub fn on(self, output: Output) -> Selector {
59        Selector {
60            surface: self,
61            output,
62        }
63    }
64
65    /// Inverts the selection to exclude matching surfaces
66    #[must_use]
67    pub fn except(self, other: impl Into<Surface>) -> Self {
68        Self::Not(Box::new(other.into()))
69    }
70
71    /// Combines this selector with another using OR logic
72    #[must_use]
73    pub fn or(self, other: impl Into<Surface>) -> Self {
74        match self {
75            Self::Or(mut selectors) => {
76                selectors.push(other.into());
77                Self::Or(selectors)
78            }
79            _ => Self::Or(vec![self, other.into()]),
80        }
81    }
82
83    pub(crate) fn matches(&self, info: &SurfaceInfo) -> bool {
84        match self {
85            Self::All => true,
86            Self::Named(name) => &info.name == name,
87            Self::Any(names) => names.iter().any(|name| name == &info.name),
88            Self::Filter(predicate) => predicate(info),
89            Self::Not(selector) => !selector.matches(info),
90            Self::Or(selectors) => selectors.iter().any(|s| s.matches(info)),
91        }
92    }
93}
94
95impl Debug for Surface {
96    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
97        match self {
98            Self::All => write!(f, "Surface::All"),
99            Self::Named(name) => write!(f, "Surface::Named({:?})", name),
100            Self::Any(names) => write!(f, "Surface::Any({:?})", names),
101            Self::Filter(_) => write!(f, "Surface::Filter(<fn>)"),
102            Self::Not(selector) => write!(f, "Surface::Not({:?})", selector),
103            Self::Or(selectors) => write!(f, "Surface::Or({:?})", selectors),
104        }
105    }
106}
107
108/// Selector for targeting outputs (monitors) in runtime operations
109#[derive(Clone)]
110pub enum Output {
111    /// Select all outputs
112    All,
113    /// Select the primary output
114    Primary,
115    /// Select the currently active output
116    Active,
117    /// Select output by handle
118    Handle(OutputHandle),
119    /// Select output by name
120    Named(String),
121    /// Select outputs matching a custom predicate
122    Filter(Arc<dyn Fn(&OutputInfo) -> bool + Send + Sync>),
123    /// Invert selection
124    Not(Box<Output>),
125    /// Union of multiple selectors
126    Or(Vec<Output>),
127}
128
129impl Output {
130    /// Selects all outputs
131    pub fn all() -> Self {
132        Self::All
133    }
134
135    /// Selects the primary output
136    pub fn primary() -> Self {
137        Self::Primary
138    }
139
140    /// Selects the currently active output
141    pub fn active() -> Self {
142        Self::Active
143    }
144
145    /// Selects an output by handle
146    pub fn handle(handle: OutputHandle) -> Self {
147        Self::Handle(handle)
148    }
149
150    /// Selects an output by name
151    pub fn named(name: impl Into<String>) -> Self {
152        Self::Named(name.into())
153    }
154
155    /// Selects outputs matching a custom predicate
156    pub fn matching<F>(predicate: F) -> Self
157    where
158        F: Fn(&OutputInfo) -> bool + Send + Sync + 'static,
159    {
160        Self::Filter(Arc::new(predicate))
161    }
162
163    /// Inverts the selection to exclude matching outputs
164    #[must_use]
165    pub fn except(self, other: impl Into<Output>) -> Self {
166        Self::Not(Box::new(other.into()))
167    }
168
169    /// Combines this selector with another using OR logic
170    #[must_use]
171    pub fn or(self, other: impl Into<Output>) -> Self {
172        match self {
173            Self::Or(mut selectors) => {
174                selectors.push(other.into());
175                Self::Or(selectors)
176            }
177            _ => Self::Or(vec![self, other.into()]),
178        }
179    }
180
181    pub(crate) fn matches(
182        &self,
183        handle: OutputHandle,
184        info: Option<&OutputInfo>,
185        primary: Option<OutputHandle>,
186        active: Option<OutputHandle>,
187    ) -> bool {
188        match self {
189            Self::All => true,
190            Self::Primary => primary == Some(handle),
191            Self::Active => active == Some(handle),
192            Self::Handle(h) => *h == handle,
193            Self::Named(name) => info.is_some_and(|i| i.name() == Some(name.as_str())),
194            Self::Filter(predicate) => info.is_some_and(|i| predicate(i)),
195            Self::Not(selector) => !selector.matches(handle, info, primary, active),
196            Self::Or(selectors) => selectors
197                .iter()
198                .any(|s| s.matches(handle, info, primary, active)),
199        }
200    }
201}
202
203impl Debug for Output {
204    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
205        match self {
206            Self::All => write!(f, "Output::All"),
207            Self::Primary => write!(f, "Output::Primary"),
208            Self::Active => write!(f, "Output::Active"),
209            Self::Handle(h) => write!(f, "Output::Handle({:?})", h),
210            Self::Named(name) => write!(f, "Output::Named({:?})", name),
211            Self::Filter(_) => write!(f, "Output::Filter(<fn>)"),
212            Self::Not(selector) => write!(f, "Output::Not({:?})", selector),
213            Self::Or(selectors) => write!(f, "Output::Or({:?})", selectors),
214        }
215    }
216}
217
218/// Combined surface and output selector for precise targeting
219///
220/// Targets specific surface instances on specific outputs.
221#[derive(Clone, Debug)]
222pub struct Selector {
223    pub surface: Surface,
224    pub output: Output,
225}
226
227impl Selector {
228    /// Creates a selector matching all surfaces on all outputs
229    pub fn all() -> Self {
230        Self {
231            surface: Surface::All,
232            output: Output::All,
233        }
234    }
235
236    pub(crate) fn matches(
237        &self,
238        surface_info: &SurfaceInfo,
239        output_info: Option<&OutputInfo>,
240        primary: Option<OutputHandle>,
241        active: Option<OutputHandle>,
242    ) -> bool {
243        self.surface.matches(surface_info)
244            && self
245                .output
246                .matches(surface_info.output, output_info, primary, active)
247    }
248}
249
250impl From<Surface> for Selector {
251    fn from(surface: Surface) -> Self {
252        Self {
253            surface,
254            output: Output::All,
255        }
256    }
257}
258
259impl From<Output> for Selector {
260    fn from(output: Output) -> Self {
261        Self {
262            surface: Surface::All,
263            output,
264        }
265    }
266}