1use 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#[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#[cfg(docsrs)]
47pub type SpellSkiaWinAdapter = SpellSkiaWinAdapterDummy;
48
49pub struct SpellWinAdapter {
53 pub window: Window,
54 pub rendered: SoftwareRenderer,
55 pub size: PhysicalSize,
56 pub needs_redraw: Cell<bool>,
57}
58
59#[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 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
105pub struct SpellLayerShell {
108 pub window_adapter: Rc<SpellSkiaWinAdapter>,
110 pub span: span::Span,
111}
112
113impl SpellLayerShell {
114 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
145pub struct SpellMultiLayerShell {
150 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
181pub 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 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 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}