Skip to main content

ferroui_base/rendering/composition/
render_interface_feature.rs

1//! A feature of the render interface, as a caller holds it.
2//!
3//! Upstream `Compositor.TryGetRenderInterfaceFeature` hands the feature
4//! object itself to the UI thread, which keeps it while the render thread
5//! keeps the map it came from; the garbage collector shares the object. Here
6//! a feature is an `Rc` in the map of the server compositor, and a second
7//! handle to it held by another thread would change the same count without
8//! synchronisation. So the caller gets a [`RenderInterfaceFeature`]: the
9//! handle stays inside the compositor lock (a [`LockBound`]), and the feature
10//! is lent from there for the length of a call.
11
12use super::server::{LockBound, ServerCompositor};
13use std::any::Any;
14use std::rc::Rc;
15
16/// A feature of the platform render interface
17/// ([`Compositor::try_get_render_interface_feature`](super::Compositor::try_get_render_interface_feature)).
18///
19/// The handle keeps the feature alive, as the object upstream does: a caller
20/// that holds it still reaches the feature of a context that has since been
21/// replaced. It may be kept, moved and dropped anywhere; the feature behind
22/// it is only reached inside the compositor lock, with
23/// [`with`](Self::with) from outside or with [`get`](Self::get) by a caller
24/// that already has the server compositor in hand (a job).
25///
26/// A feature is registered under the type it is asked for by. By the
27/// convention of the optional features a feature asked for as a trait object
28/// (`TypeId::of::<dyn IFoo>()`) is registered as an `Rc<dyn IFoo>`: `with`
29/// and `get` lend the `dyn IFoo`, never the `Rc`.
30pub struct RenderInterfaceFeature {
31    feature: LockBound<Rc<dyn Any>>,
32}
33
34impl RenderInterfaceFeature {
35    /// `feature` is a handle cloned from the map of features inside the
36    /// lock, bound to the lock there.
37    pub(crate) fn new(feature: LockBound<Rc<dyn Any>>) -> Self {
38        Self { feature }
39    }
40
41    fn lend<T: ?Sized + 'static>(feature: &Rc<dyn Any>) -> Option<&T> {
42        feature.downcast_ref::<Rc<T>>().map(|feature| &**feature)
43    }
44
45    /// Runs `f` with the feature, registered as an `Rc<T>`, inside the
46    /// compositor lock, which the calling thread enters for the call.
47    ///
48    /// `None` when the feature is not registered as an `Rc<T>`, and once the
49    /// compositor has released its server compositor.
50    pub fn with<T: ?Sized + 'static, R>(&self, f: impl FnOnce(&T) -> R) -> Option<R> {
51        self.feature.with(|_, feature| Self::lend::<T>(feature).map(f)).flatten()
52    }
53
54    /// The feature, registered as an `Rc<T>`, for a caller that is inside
55    /// the compositor lock: `server` is what a job receives and what
56    /// `Compositor::with_server` hands out. `None` when the feature is not
57    /// registered as an `Rc<T>`.
58    ///
59    /// # Panics
60    ///
61    /// When `server` is not the server compositor the feature came from.
62    pub fn get<'a, T: ?Sized + 'static>(&'a self, server: &'a ServerCompositor) -> Option<&'a T> {
63        Self::lend::<T>(self.feature.get(server))
64    }
65
66    /// Whether the feature is registered as a value of type `T` (for a
67    /// feature asked for as a trait object, `Rc<dyn IFoo>`). `false` once
68    /// the compositor has released its server compositor.
69    pub fn is<T: 'static>(&self) -> bool {
70        self.feature.with(|_, feature| feature.is::<T>()).unwrap_or(false)
71    }
72}
73
74const _: fn() = || {
75    // The handle is what crosses out of the lock, so it may be held by any
76    // thread.
77    fn assert_send_sync<T: Send + Sync>() {}
78    assert_send_sync::<RenderInterfaceFeature>();
79};