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::{
10    PhysicalSize, Window,
11    platform::{
12        EventLoopProxy, Platform, WindowAdapter,
13        software_renderer::{
14            RepaintBufferType::{self},
15            SoftwareRenderer,
16        },
17    },
18};
19use smithay_client_toolkit::reexports::client::Connection;
20use std::{
21    cell::{Cell, RefCell},
22    rc::Rc,
23    sync::{Arc, Mutex},
24};
25use tracing::{Level, info, span, warn};
26
27#[cfg(not(docsrs))]
28#[cfg(feature = "i-slint-renderer-skia")]
29use crate::skia_non_docs::SpellSkiaWinAdapterReal;
30
31/// It is the main struct handling the rendering of pixels in the wayland window. It implements slint's
32/// [WindowAdapter](https://docs.rs/slint/latest/slint/platform/trait.WindowAdapter.html) trait.
33/// It is used internally by [SpellMultiWinHandler] and previously by [SpellLayerShell]. This
34/// adapter internally uses [Skia](https://skia.org/) 2D graphics library for rendering.
35#[cfg(not(docsrs))]
36#[cfg(feature = "i-slint-renderer-skia")]
37pub type SpellSkiaWinAdapter = SpellSkiaWinAdapterReal;
38
39#[cfg(docsrs)]
40use crate::dummy_skia_docs::SpellSkiaWinAdapterDummy;
41
42/// It is the main struct handling the rendering of pixels in the wayland window. It implements slint's
43/// [WindowAdapter](https://docs.rs/slint/latest/slint/platform/trait.WindowAdapter.html) trait.
44/// It is used internally by [SpellMultiWinHandler] and previously by [SpellLayerShell]. This
45/// adapter internally uses [Skia](https://skia.org/) 2D graphics library for rendering.
46#[cfg(docsrs)]
47pub type SpellSkiaWinAdapter = SpellSkiaWinAdapterDummy;
48
49/// This was the previous struct used internally for rendering, its use is stopped in favour of
50/// [SpellSkiaWinAdapter] which provides faster and more reliable rendering. It implements slint's
51/// [WindowAdapter](https://docs.rs/slint/latest/slint/platform/trait.WindowAdapter.html) trait.
52pub struct SpellWinAdapter {
53    pub window: Window,
54    pub rendered: SoftwareRenderer,
55    pub size: PhysicalSize,
56    pub needs_redraw: Cell<bool>,
57}
58
59// TODO this dead code needs to be either removed or imporoved.
60#[allow(dead_code)]
61impl SpellWinAdapter {
62    fn new(width: u32, height: u32) -> Rc<Self> {
63        Rc::<SpellWinAdapter>::new_cyclic(|adapter| SpellWinAdapter {
64            window: Window::new(adapter.clone()),
65            rendered: SoftwareRenderer::new_with_repaint_buffer_type(
66                RepaintBufferType::ReusedBuffer,
67            ),
68            size: PhysicalSize { width, height },
69            needs_redraw: Default::default(),
70        })
71    }
72
73    fn draw_if_needed(&self, render_callback: impl FnOnce(&SoftwareRenderer)) -> bool {
74        if self.needs_redraw.replace(false) {
75            render_callback(&self.rendered);
76            true
77        } else {
78            false
79        }
80    }
81}
82
83impl WindowAdapter for SpellWinAdapter {
84    fn window(&self) -> &Window {
85        &self.window
86    }
87
88    fn size(&self) -> PhysicalSize {
89        // This value have to be made dynamic by using `xandr`
90        PhysicalSize {
91            width: self.size.width,
92            height: self.size.height,
93        }
94    }
95
96    fn renderer(&self) -> &dyn slint::platform::Renderer {
97        &self.rendered
98    }
99
100    fn request_redraw(&self) {
101        self.needs_redraw.set(true);
102    }
103}
104
105/// Previously needed to be implemented, now this struct is called and set internally
106/// when [`invoke_spell`](crate::wayland_adapter::SpellWin::invoke_spell) is called.
107pub struct SpellLayerShell {
108    /// An instance of [SpellSkiaWinAdapter].
109    pub window_adapter: Rc<SpellSkiaWinAdapter>,
110    pub span: span::Span,
111}
112
113impl SpellLayerShell {
114    /// Creates an instance of this Platform implementation, for internal use.
115    pub fn new(window_adapter: Rc<SpellSkiaWinAdapter>) -> Self {
116        SpellLayerShell {
117            window_adapter,
118            span: span!(Level::INFO, "slint-log",),
119        }
120    }
121}
122
123impl Platform for SpellLayerShell {
124    fn create_window_adapter(&self) -> Result<Rc<dyn WindowAdapter>, slint::PlatformError> {
125        Ok(self.window_adapter.clone())
126    }
127
128    fn debug_log(&self, arguments: core::fmt::Arguments) {
129        self.span.in_scope(|| {
130            if let Some(val) = arguments.as_str() {
131                info!(val);
132            } else {
133                info!("{}", arguments.to_string());
134            }
135        })
136    }
137
138    fn new_event_loop_proxy(&self) -> Option<Box<dyn EventLoopProxy>> {
139        Some(Box::new(SlintEventProxy(
140            self.window_adapter.slint_event_proxy.clone(),
141        )))
142    }
143}
144
145/// This struct needs to be set when multiple windows are to be started together. It is
146/// used in combination with [`conjure_spells`](crate::slint_adapter::SpellMultiWinHandler::conjure_spells)
147/// and is required to be set before any other initialisation with an instance of [SpellMultiWinHandler].
148/// It implements slint's [Platform](https://docs.rs/slint/latest/slint/platform/trait.Platform.html) trait and is set internally.
149pub struct SpellMultiLayerShell {
150    /// An instance of [SpellMultiWinHandler].
151    pub window_manager: Rc<RefCell<SpellMultiWinHandler>>,
152    pub span: span::Span,
153}
154
155impl SpellMultiLayerShell {
156    fn new(window_manager: Rc<RefCell<SpellMultiWinHandler>>) -> Self {
157        SpellMultiLayerShell {
158            window_manager,
159            span: span!(Level::INFO, "slint-log",),
160        }
161    }
162}
163
164impl Platform for SpellMultiLayerShell {
165    fn create_window_adapter(&self) -> Result<Rc<dyn WindowAdapter>, slint::PlatformError> {
166        let value = self.window_manager.borrow_mut().request_new_window();
167        Ok(value)
168    }
169
170    fn debug_log(&self, arguments: core::fmt::Arguments) {
171        self.span.in_scope(|| {
172            if let Some(val) = arguments.as_str() {
173                info!(val);
174            } else {
175                info!("{}", arguments.to_string());
176            }
177        })
178    }
179}
180
181/// Used for the initialisation of [SpellMultiLayerShell], this struct is responsible
182/// for handling, initialising, updating and maintaing of various widgets that are being
183/// rendered simultaneously. It uses [SpellSkiaWinAdapter] internally.
184pub struct SpellMultiWinHandler {
185    pub(crate) windows: Vec<(String, LayerConf)>,
186    pub(crate) adapter: Vec<Rc<SpellSkiaWinAdapter>>,
187    pub(crate) value_given: u32,
188}
189
190impl SpellMultiWinHandler {
191    /// This function is finally called to create instances of windows (in a multi
192    /// window scenario). These windows are ultimately passed on to [enchant_spells](`crate::enchant_spells`)
193    /// event loop, multi-window setup is unstable though and is not recommended for end use just
194    /// now.
195    pub fn conjure_spells(windows: Vec<(&str, WindowConf)>) -> Vec<SpellWin> {
196        tracing_subscriber::fmt()
197            .without_time()
198            .with_target(false)
199            .with_env_filter(tracing_subscriber::EnvFilter::new(
200                "spell_framework=trace,info",
201            ))
202            .init();
203        let handle = set_up_tracing("multi-window");
204        let conn = Connection::connect_to_env().unwrap();
205        let new_windows: Vec<(String, LayerConf)> = windows
206            .iter()
207            .map(|(layer_name, conf)| (layer_name.to_string(), LayerConf::Window(conf.clone())))
208            .collect();
209
210        let mut new_adapters: Vec<Rc<SpellSkiaWinAdapter>> = Vec::new();
211        let mut windows_spell: Vec<SpellWin> = Vec::new();
212        windows.iter().for_each(|(layer_name, conf)| {
213            let window = SpellWin::create_window(
214                &conn,
215                conf.clone(),
216                layer_name.to_string(),
217                false,
218                handle.clone(),
219            );
220            let adapter = window.adapter.clone();
221            windows_spell.push(window);
222            new_adapters.push(adapter);
223        });
224        let windows_handler = Rc::new(RefCell::new(SpellMultiWinHandler {
225            windows: new_windows,
226            adapter: new_adapters,
227            value_given: 0,
228        }));
229
230        if let Err(error) =
231            slint::platform::set_platform(Box::new(SpellMultiLayerShell::new(windows_handler)))
232        {
233            warn!("Error setting multilayer platform: {}", error);
234        }
235        windows_spell
236    }
237
238    pub(crate) fn new_lock(lock_outputs: Vec<(String, (u32, u32))>) -> Rc<RefCell<Self>> {
239        let new_locks: Vec<(String, LayerConf)> = lock_outputs
240            .iter()
241            .map(|(output_name, conf)| (output_name.clone(), LayerConf::Lock(conf.0, conf.1)))
242            .collect();
243
244        Rc::new(RefCell::new(SpellMultiWinHandler {
245            windows: new_locks,
246            adapter: Vec::new(),
247            value_given: 0,
248        }))
249    }
250
251    fn request_new_window(&mut self) -> Rc<dyn WindowAdapter> {
252        self.value_given += 1;
253        let index = self.value_given - 1;
254        self.adapter[index as usize].clone()
255    }
256
257    fn request_new_lock(&mut self) -> Rc<dyn WindowAdapter> {
258        self.value_given += 1;
259        let index = self.value_given - 1;
260        self.adapter[index as usize].clone()
261    }
262}
263
264pub struct SpellLockShell {
265    /// An instance of [SpellMultiWinHandler].
266    pub window_manager: Rc<RefCell<SpellMultiWinHandler>>,
267    pub span: span::Span,
268}
269
270impl SpellLockShell {
271    pub fn new(window_manager: Rc<RefCell<SpellMultiWinHandler>>) -> Self {
272        SpellLockShell {
273            window_manager,
274            span: span!(Level::INFO, "slint-lock-log",),
275        }
276    }
277}
278
279impl Platform for SpellLockShell {
280    fn create_window_adapter(&self) -> Result<Rc<dyn WindowAdapter>, slint::PlatformError> {
281        let value = self.window_manager.borrow_mut().request_new_lock();
282        Ok(value)
283    }
284
285    fn debug_log(&self, arguments: core::fmt::Arguments) {
286        self.span.in_scope(|| {
287            if let Some(val) = arguments.as_str() {
288                info!(val);
289            } else {
290                info!("{}", arguments.to_string());
291            }
292        })
293    }
294}
295
296#[allow(clippy::type_complexity)]
297struct SlintEventProxy(Arc<Mutex<Vec<Box<dyn FnOnce() + Send>>>>);
298
299impl EventLoopProxy for SlintEventProxy {
300    fn quit_event_loop(&self) -> Result<(), i_slint_core::api::EventLoopError> {
301        Ok(())
302    }
303
304    fn invoke_from_event_loop(
305        &self,
306        event: Box<dyn FnOnce() + Send>,
307    ) -> Result<(), i_slint_core::api::EventLoopError> {
308        if let Ok(mut list_of_event) = self.0.try_lock() {
309            (*list_of_event).push(event);
310        } else {
311            warn!("Slint proxy event could not be processed");
312        }
313        Ok(())
314    }
315}