Skip to main content

spell_framework/
slint_adapter.rs

1//! This module contains relevent structs for slint side backend configurations. Apart
2//! from [SpellMultiLayerShell] and [SpellMultiWinHandler], rest of the structs mentioned are
3//! either internal or not used anymore. Still their implementation is public because they had be
4//! set by the user of library in intial iterations of spell_framework.
5use crate::{
6    configure::{LayerConf, WindowConf, set_up_tracing},
7    wayland_adapter::SpellWin,
8};
9use slint::platform::{EventLoopProxy, Platform, WindowAdapter};
10use smithay_client_toolkit::reexports::client::Connection;
11use std::{
12    cell::RefCell,
13    rc::Rc,
14    sync::{Arc, Mutex},
15};
16use tracing::{Level, info, span, warn};
17
18#[cfg(not(docsrs))]
19#[cfg(feature = "i-slint-renderer-skia")]
20use crate::skia_non_docs::SpellSkiaWinAdapterReal;
21
22/// It is the main struct handling the rendering of pixels in the wayland window. It implements slint's
23/// [WindowAdapter](https://docs.rs/slint/latest/slint/platform/trait.WindowAdapter.html) trait.
24/// It is used internally by [SpellMultiWinHandler] and previously by [SpellLayerShell]. This
25/// adapter internally uses [Skia](https://skia.org/) 2D graphics library for rendering.
26#[cfg(not(docsrs))]
27#[cfg(feature = "i-slint-renderer-skia")]
28pub type SpellSkiaWinAdapter = SpellSkiaWinAdapterReal;
29
30#[cfg(docsrs)]
31use crate::dummy_skia_docs::SpellSkiaWinAdapterDummy;
32
33/// It is the main struct handling the rendering of pixels in the wayland window. It implements slint's
34/// [WindowAdapter](https://docs.rs/slint/latest/slint/platform/trait.WindowAdapter.html) trait.
35/// It is used internally by [SpellMultiWinHandler] and previously by [SpellLayerShell]. This
36/// adapter internally uses [Skia](https://skia.org/) 2D graphics library for rendering.
37#[cfg(docsrs)]
38pub type SpellSkiaWinAdapter = SpellSkiaWinAdapterDummy;
39/// Previously needed to be implemented, now this struct is called and set internally
40/// when [`invoke_spell`](crate::wayland_adapter::SpellWin::invoke_spell) is called.
41pub struct SpellLayerShell {
42    /// An instance of [SpellSkiaWinAdapter].
43    pub window_adapter: Rc<SpellSkiaWinAdapter>,
44    pub span: span::Span,
45}
46
47impl SpellLayerShell {
48    /// Creates an instance of this Platform implementation, for internal use.
49    pub fn new(window_adapter: Rc<SpellSkiaWinAdapter>) -> Self {
50        SpellLayerShell {
51            window_adapter,
52            span: span!(Level::INFO, "slint-log",),
53        }
54    }
55}
56
57impl Platform for SpellLayerShell {
58    fn create_window_adapter(&self) -> Result<Rc<dyn WindowAdapter>, slint::PlatformError> {
59        Ok(self.window_adapter.clone())
60    }
61
62    fn debug_log(&self, arguments: core::fmt::Arguments) {
63        self.span.in_scope(|| {
64            if let Some(val) = arguments.as_str() {
65                info!(val);
66            } else {
67                info!("{}", arguments.to_string());
68            }
69        })
70    }
71
72    fn new_event_loop_proxy(&self) -> Option<Box<dyn EventLoopProxy>> {
73        Some(Box::new(SlintEventProxy(
74            self.window_adapter.slint_event_proxy.clone(),
75        )))
76    }
77}
78
79/// This struct needs to be set when multiple windows are to be started together. It is
80/// used in combination with [`conjure_spells`](crate::slint_adapter::SpellMultiWinHandler::conjure_spells)
81/// and is required to be set before any other initialisation with an instance of [SpellMultiWinHandler].
82/// It implements slint's [Platform](https://docs.rs/slint/latest/slint/platform/trait.Platform.html) trait and is set internally.
83pub struct SpellMultiLayerShell {
84    /// An instance of [SpellMultiWinHandler].
85    pub window_manager: Rc<RefCell<SpellMultiWinHandler>>,
86    pub span: span::Span,
87}
88
89impl SpellMultiLayerShell {
90    fn new(window_manager: Rc<RefCell<SpellMultiWinHandler>>) -> Self {
91        SpellMultiLayerShell {
92            window_manager,
93            span: span!(Level::INFO, "slint-log",),
94        }
95    }
96}
97
98impl Platform for SpellMultiLayerShell {
99    fn create_window_adapter(&self) -> Result<Rc<dyn WindowAdapter>, slint::PlatformError> {
100        let value = self.window_manager.borrow_mut().request_new_window();
101        Ok(value)
102    }
103
104    fn debug_log(&self, arguments: core::fmt::Arguments) {
105        self.span.in_scope(|| {
106            if let Some(val) = arguments.as_str() {
107                info!(val);
108            } else {
109                info!("{}", arguments.to_string());
110            }
111        })
112    }
113}
114
115/// Used for the initialisation of [SpellMultiLayerShell], this struct is responsible
116/// for handling, initialising, updating and maintaing of various widgets that are being
117/// rendered simultaneously. It uses [SpellSkiaWinAdapter] internally.
118pub struct SpellMultiWinHandler {
119    pub(crate) windows: Vec<(String, LayerConf)>,
120    pub(crate) adapter: Vec<Rc<SpellSkiaWinAdapter>>,
121    pub(crate) value_given: u32,
122}
123
124impl SpellMultiWinHandler {
125    /// This function is finally called to create instances of windows (in a multi
126    /// window scenario). These windows are ultimately passed on to [enchant_spells](`crate::enchant_spells`)
127    /// event loop, multi-window setup is unstable though and is not recommended for end use just
128    /// now.
129    pub fn conjure_spells(windows: Vec<(&str, WindowConf)>) -> Vec<SpellWin> {
130        tracing_subscriber::fmt()
131            .without_time()
132            .with_target(false)
133            .with_env_filter(tracing_subscriber::EnvFilter::new(
134                "spell_framework=trace,info",
135            ))
136            .init();
137        let handle = set_up_tracing("multi-window");
138        let conn = Connection::connect_to_env().unwrap();
139        let new_windows: Vec<(String, LayerConf)> = windows
140            .iter()
141            .map(|(layer_name, conf)| (layer_name.to_string(), LayerConf::Window(conf.clone())))
142            .collect();
143
144        let mut new_adapters: Vec<Rc<SpellSkiaWinAdapter>> = Vec::new();
145        let mut windows_spell: Vec<SpellWin> = Vec::new();
146        windows.iter().for_each(|(layer_name, conf)| {
147            let window = SpellWin::create_window(
148                &conn,
149                conf.clone(),
150                layer_name.to_string(),
151                false,
152                handle.clone(),
153            );
154            new_adapters.push(window.adapter.clone());
155            windows_spell.push(window);
156        });
157        let windows_handler = Rc::new(RefCell::new(SpellMultiWinHandler {
158            windows: new_windows,
159            adapter: new_adapters,
160            value_given: 0,
161        }));
162
163        if let Err(error) =
164            slint::platform::set_platform(Box::new(SpellMultiLayerShell::new(windows_handler)))
165        {
166            warn!("Error setting multilayer platform: {}", error);
167        }
168        windows_spell
169    }
170
171    pub(crate) fn new_lock(lock_outputs: Vec<(String, (u32, u32))>) -> Rc<RefCell<Self>> {
172        let new_locks: Vec<(String, LayerConf)> = lock_outputs
173            .iter()
174            .map(|(output_name, conf)| (output_name.clone(), LayerConf::Lock(conf.0, conf.1)))
175            .collect();
176
177        Rc::new(RefCell::new(SpellMultiWinHandler {
178            windows: new_locks,
179            adapter: Vec::new(),
180            value_given: 0,
181        }))
182    }
183
184    fn request_new_window(&mut self) -> Rc<dyn WindowAdapter> {
185        self.value_given += 1;
186        let index = self.value_given - 1;
187        self.adapter[index as usize].clone()
188    }
189
190    fn request_new_lock(&mut self) -> Rc<dyn WindowAdapter> {
191        self.value_given += 1;
192        let index = self.value_given - 1;
193        self.adapter[index as usize].clone()
194    }
195}
196
197pub struct SpellLockShell {
198    /// An instance of [SpellMultiWinHandler].
199    pub window_manager: Rc<RefCell<SpellMultiWinHandler>>,
200    pub span: span::Span,
201}
202
203impl SpellLockShell {
204    pub fn new(window_manager: Rc<RefCell<SpellMultiWinHandler>>) -> Self {
205        SpellLockShell {
206            window_manager,
207            span: span!(Level::INFO, "slint-lock-log",),
208        }
209    }
210}
211
212impl Platform for SpellLockShell {
213    fn create_window_adapter(&self) -> Result<Rc<dyn WindowAdapter>, slint::PlatformError> {
214        let value = self.window_manager.borrow_mut().request_new_lock();
215        Ok(value)
216    }
217
218    fn debug_log(&self, arguments: core::fmt::Arguments) {
219        self.span.in_scope(|| {
220            if let Some(val) = arguments.as_str() {
221                info!(val);
222            } else {
223                info!("{}", arguments.to_string());
224            }
225        })
226    }
227}
228
229#[allow(clippy::type_complexity)]
230struct SlintEventProxy(Arc<Mutex<Vec<Box<dyn FnOnce() + Send>>>>);
231
232impl EventLoopProxy for SlintEventProxy {
233    fn quit_event_loop(&self) -> Result<(), i_slint_core::api::EventLoopError> {
234        Ok(())
235    }
236
237    fn invoke_from_event_loop(
238        &self,
239        event: Box<dyn FnOnce() + Send>,
240    ) -> Result<(), i_slint_core::api::EventLoopError> {
241        if let Ok(mut list_of_event) = self.0.try_lock() {
242            (*list_of_event).push(event);
243        } else {
244            warn!("Slint proxy event could not be processed");
245        }
246        Ok(())
247    }
248}