spell_framework/
slint_adapter.rs1use crate::configure::LayerConf;
6use slint::platform::{EventLoopProxy, Platform, WindowAdapter};
7use smithay_client_toolkit::reexports::calloop;
8use std::{cell::RefCell, io::Read, rc::Rc};
9use tracing::{Level, info, span, warn};
10use wl_clipboard_rs::{
11 copy::{MimeType as CopyMimeType, Options, Source},
12 paste::{ClipboardType, Error, MimeType as PasteMimeType, Seat, get_contents},
13};
14
15thread_local! {
16 pub(crate) static ADAPTERS: RefCell<Vec<Rc<SpellSkiaWinAdapter>>> = const { RefCell::new(Vec::new()) };
17}
18
19#[cfg(not(docsrs))]
20#[cfg(feature = "i-slint-renderer-skia")]
21use crate::skia_non_docs::SpellSkiaWinAdapterReal;
22
23#[cfg(not(docsrs))]
28#[cfg(feature = "i-slint-renderer-skia")]
29pub type SpellSkiaWinAdapter = SpellSkiaWinAdapterReal;
30
31#[cfg(docsrs)]
32use crate::dummy_skia_docs::SpellSkiaWinAdapterDummy;
33
34#[cfg(docsrs)]
39pub type SpellSkiaWinAdapter = SpellSkiaWinAdapterDummy;
40
41pub struct SpellLayerShell {
44 pub span: span::Span,
46 slint_event_sender: calloop::channel::Sender<Box<dyn FnOnce() + Send>>,
47}
48
49impl SpellLayerShell {
50 pub(crate) fn new(
52 slint_event_sender: calloop::channel::Sender<Box<dyn FnOnce() + Send>>,
53 ) -> Self {
54 Self {
55 span: span!(Level::INFO, "slint-log",),
56 slint_event_sender,
57 }
58 }
59}
60
61impl Platform for SpellLayerShell {
71 fn create_window_adapter(&self) -> Result<Rc<dyn WindowAdapter>, slint::PlatformError> {
72 let adapter = ADAPTERS.with(|v| v.borrow().last().unwrap().clone());
73 Ok(adapter)
74 }
75
76 fn debug_log(&self, arguments: core::fmt::Arguments) {
77 self.span.in_scope(|| {
78 if let Some(val) = arguments.as_str() {
79 info!(val);
80 } else {
81 info!("{}", arguments.to_string());
82 }
83 })
84 }
85
86 fn new_event_loop_proxy(&self) -> Option<Box<dyn EventLoopProxy>> {
87 Some(Box::new(SlintEventProxy(self.slint_event_sender.clone())))
88 }
89
90 fn set_clipboard_text(&self, text: &str, _clipboard: slint::platform::Clipboard) {
93 let opts = Options::new();
95 if let Err(err) = opts.copy(
96 Source::Bytes(text.to_string().into_bytes().into()),
97 CopyMimeType::Autodetect,
98 ) {
99 warn!("[Clipboard]: Error in setting clipboard value: {}", err);
100 } else {
101 info!("[Clipboard]: Successfully copied text");
102 }
103 }
105
106 fn clipboard_text(&self, _clipboard: slint::platform::Clipboard) -> Option<String> {
109 let result = get_contents(
110 ClipboardType::Regular,
111 Seat::Unspecified,
112 PasteMimeType::Text,
113 );
114 match result {
115 Ok((mut pipe, _)) => {
116 let mut contents = vec![];
117 pipe.read_to_end(&mut contents).unwrap();
119 let text = String::from_utf8_lossy(&contents).to_string();
120 info!("[Clipboard]: Successfully pasted text: {}", text);
121 Some(text)
122 }
123
124 Err(Error::NoSeats) | Err(Error::ClipboardEmpty) | Err(Error::NoMimeType) => {
126 warn!("[Clipboard]: Clipboard was either empty or didn't have text type data");
127 Some("".to_string())
128 }
129
130 Err(err) => {
131 warn!("[Clipboard]: error getting clipboard text: {}", err);
132 None
133 }
134 }
135 }
136}
137
138pub struct SpellMultiWinHandler {
144 pub(crate) windows: Vec<(String, LayerConf)>,
145 pub(crate) adapter: Vec<Rc<SpellSkiaWinAdapter>>,
146 pub(crate) value_given: u32,
147}
148
149impl SpellMultiWinHandler {
150 pub(crate) fn new_lock(lock_outputs: Vec<(String, (u32, u32))>) -> Rc<RefCell<Self>> {
151 let new_locks: Vec<(String, LayerConf)> = lock_outputs
152 .iter()
153 .map(|(output_name, conf)| (output_name.clone(), LayerConf::Lock(conf.0, conf.1)))
154 .collect();
155
156 Rc::new(RefCell::new(SpellMultiWinHandler {
157 windows: new_locks,
158 adapter: Vec::new(),
159 value_given: 0,
160 }))
161 }
162
163 fn request_new_lock(&mut self) -> Rc<dyn WindowAdapter> {
164 self.value_given += 1;
165 let index = self.value_given - 1;
166 self.adapter[index as usize].clone()
167 }
168}
169
170pub struct SpellLockShell {
173 pub window_manager: Rc<RefCell<SpellMultiWinHandler>>,
175 pub slint_event_sender: calloop::channel::Sender<Box<dyn FnOnce() + Send>>,
177 pub span: span::Span,
180}
181
182impl SpellLockShell {
183 pub fn new(
186 window_manager: Rc<RefCell<SpellMultiWinHandler>>,
187 slint_event_sender: calloop::channel::Sender<Box<dyn FnOnce() + Send>>,
188 ) -> Self {
189 SpellLockShell {
190 slint_event_sender,
191 window_manager,
192 span: span!(Level::INFO, "slint-lock-log",),
193 }
194 }
195}
196
197impl Platform for SpellLockShell {
198 fn create_window_adapter(&self) -> Result<Rc<dyn WindowAdapter>, slint::PlatformError> {
199 let value = self.window_manager.borrow_mut().request_new_lock();
200 Ok(value)
201 }
202
203 fn new_event_loop_proxy(&self) -> Option<Box<dyn EventLoopProxy>> {
204 Some(Box::new(SlintEventProxy(self.slint_event_sender.clone())))
205 }
206
207 fn debug_log(&self, arguments: core::fmt::Arguments) {
208 self.span.in_scope(|| {
209 if let Some(val) = arguments.as_str() {
210 info!(val);
211 } else {
212 info!("{}", arguments.to_string());
213 }
214 })
215 }
216}
217
218struct SlintEventProxy(calloop::channel::Sender<Box<dyn FnOnce() + Send>>);
219
220impl EventLoopProxy for SlintEventProxy {
221 fn quit_event_loop(&self) -> Result<(), i_slint_core::api::EventLoopError> {
222 Ok(())
223 }
224
225 fn invoke_from_event_loop(
226 &self,
227 event: Box<dyn FnOnce() + Send>,
228 ) -> Result<(), i_slint_core::api::EventLoopError> {
229 self.0
230 .send(event)
231 .map_err(|_| i_slint_core::api::EventLoopError::EventLoopTerminated)
232 }
233}