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