1use slint::platform::software_renderer::TargetPixel;
2use smithay_client_toolkit::shell::wlr_layer::{Anchor, KeyboardInteractivity, Layer};
3use std::{cell::Cell, fs, io::Write, os::unix::net::UnixDatagram, path::Path, sync::Mutex};
4use tracing::warn;
5use tracing_appender::rolling::{RollingFileAppender, Rotation};
6use tracing_subscriber::{
7 EnvFilter, Layer as TracingTraitLayer,
8 filter::Filtered,
9 fmt::{self, format::DefaultFields},
10 layer::{Layered, SubscriberExt},
11 registry::Registry,
12 reload::Layer as LoadLayer,
13};
14
15#[allow(dead_code)]
18#[derive(Default)]
19pub struct Rgba8Pixel {
20 pub a: u8,
21 pub r: u8,
22 pub g: u8,
23 pub b: u8,
24}
25
26impl Rgba8Pixel {
27 pub fn new(a: u8, r: u8, g: u8, b: u8) -> Self {
28 Rgba8Pixel { a, r, g, b }
29 }
30}
31
32impl TargetPixel for Rgba8Pixel {
33 fn blend(&mut self, color: slint::platform::software_renderer::PremultipliedRgbaColor) {
34 let a: u16 = (u8::MAX - color.alpha) as u16;
35 let out_a = color.alpha as u16 + (self.a as u16 * (255 - color.alpha) as u16) / 255;
37 self.a = out_a as u8;
38 self.r = (self.r as u16 * a / 255) as u8 + color.red;
39 self.g = (self.g as u16 * a / 255) as u8 + color.green;
40 self.b = (self.b as u16 * a / 255) as u8 + color.blue;
41 }
42
43 fn from_rgb(red: u8, green: u8, blue: u8) -> Self {
44 let a = 0xFF;
45 Self::new(a, red, green, blue)
46 }
47
48 fn background() -> Self {
49 let a: u8 = 0x00;
53 Self::new(a, 0, 0, 0)
54 }
55}
56
57impl std::marker::Copy for Rgba8Pixel {}
58impl std::clone::Clone for Rgba8Pixel {
59 fn clone(&self) -> Self {
60 *self
61 }
62}
63
64#[derive(Debug, Clone)]
71pub struct WindowConf {
72 pub width: u32,
77 pub height: u32,
82 pub anchor: (Option<Anchor>, Option<Anchor>),
86 pub margin: (i32, i32, i32, i32),
90 pub layer_type: Layer,
93 pub board_interactivity: Cell<KeyboardInteractivity>,
96 pub exclusive_zone: Option<i32>,
99 pub monitor_name: Option<String>,
102 pub natural_scroll: bool,
106}
107
108impl WindowConf {
109 #[deprecated(
111 since = "1.0.2",
112 note = "Use the builder method to access all the configuration. It will be removed in release 1.0.3."
113 )]
114 pub fn new(
115 max_width: u32,
116 max_height: u32,
117 anchor: (Option<Anchor>, Option<Anchor>),
118 margin: (i32, i32, i32, i32),
119 layer_type: Layer,
120 board_interactivity: KeyboardInteractivity,
121 exclusive_zone: Option<i32>,
122 ) -> Self {
123 WindowConf {
124 width: max_width,
125 height: max_height,
126 anchor,
127 margin,
128 layer_type,
129 board_interactivity: Cell::new(board_interactivity),
130 exclusive_zone,
131 monitor_name: None,
132 natural_scroll: false,
133 }
134 }
135
136 pub fn builder() -> WindowConfBuilder {
139 WindowConfBuilder::default()
140 }
141}
142
143#[derive(Default)]
144pub struct WindowConfBuilder {
145 max_width: u32,
146 max_height: u32,
147 anchor: (Option<Anchor>, Option<Anchor>),
148 margin: (i32, i32, i32, i32),
149 layer_type: Option<Layer>,
150 board_interactivity: KeyboardInteractivity,
151 exclusive_zone: Option<i32>,
152 monitor_name: Option<String>,
153 natural_scroll: bool,
154}
155
156impl WindowConfBuilder {
157 pub fn width<I: Into<u32>>(&mut self, width: I) {
159 self.max_width = width.into();
160 }
161
162 pub fn height<I: Into<u32>>(&mut self, height: I) {
164 self.max_width = height.into();
165 }
166
167 pub fn anchor_1(&mut self, anchor: Anchor) {
169 self.anchor.0 = Some(anchor);
170 }
171
172 pub fn anchor_2(&mut self, anchor: Anchor) {
174 self.anchor.1 = Some(anchor);
175 }
176
177 pub fn margins(&mut self, top: i32, right: i32, bottom: i32, left: i32) {
179 self.margin = (top, right, bottom, left)
180 }
181
182 pub fn layer_type(&mut self, layer: Layer) {
184 self.layer_type = Some(layer);
185 }
186
187 pub fn board_interactivity(&mut self, board: KeyboardInteractivity) {
189 self.board_interactivity = board;
190 }
191
192 pub fn exclusive_zone(&mut self, dimention: i32) {
194 self.exclusive_zone = Some(dimention);
195 }
196
197 pub fn monitor(&mut self, name: String) {
199 self.monitor_name = Some(name)
200 }
201
202 pub fn natural_scroll(&mut self, scroll: bool) {
204 self.natural_scroll = scroll;
205 }
206
207 pub fn build(&self) -> Result<WindowConf, Box<dyn std::error::Error>> {
211 Ok(WindowConf {
212 width: if self.max_width != 0 {
213 self.max_width
214 } else {
215 return Err("width is either not defined or set to zero".into());
216 },
217 height: if self.max_width != 0 {
218 self.max_height
219 } else {
220 return Err("height is either not defined or set to zero".into());
221 },
222 anchor: self.anchor,
223 margin: self.margin,
224 layer_type: match self.layer_type {
225 None => Layer::Top,
226 Some(val) => val,
227 },
228 board_interactivity: Cell::new(self.board_interactivity),
229 exclusive_zone: self.exclusive_zone,
230 monitor_name: self.monitor_name.clone(),
231 natural_scroll: self.natural_scroll,
232 })
233 }
234}
235
236pub(crate) type HomeHandle = tracing_subscriber::reload::Handle<
237 Filtered<
238 tracing_subscriber::fmt::Layer<
239 Layered<
240 Filtered<
241 tracing_subscriber::fmt::Layer<
242 Layered<
243 Filtered<
244 tracing_subscriber::fmt::Layer<
245 Registry,
246 DefaultFields,
247 tracing_subscriber::fmt::format::Format<
248 tracing_subscriber::fmt::format::Full,
249 (),
250 >,
251 >,
252 EnvFilter,
253 Registry,
254 >,
255 Registry,
256 >,
257 DefaultFields,
258 tracing_subscriber::fmt::format::Format<
259 tracing_subscriber::fmt::format::Full,
260 (),
261 >,
262 RollingFileAppender,
263 >,
264 EnvFilter,
265 Layered<
266 Filtered<
267 tracing_subscriber::fmt::Layer<
268 Registry,
269 DefaultFields,
270 tracing_subscriber::fmt::format::Format<
271 tracing_subscriber::fmt::format::Full,
272 (),
273 >,
274 >,
275 EnvFilter,
276 Registry,
277 >,
278 Registry,
279 >,
280 >,
281 Layered<
282 Filtered<
283 tracing_subscriber::fmt::Layer<
284 Registry,
285 DefaultFields,
286 tracing_subscriber::fmt::format::Format<
287 tracing_subscriber::fmt::format::Full,
288 (),
289 >,
290 >,
291 EnvFilter,
292 Registry,
293 >,
294 Registry,
295 >,
296 >,
297 DefaultFields,
298 tracing_subscriber::fmt::format::Format<tracing_subscriber::fmt::format::Full, ()>,
299 std::sync::Mutex<SocketWriter>,
300 >,
301 EnvFilter,
302 Layered<
303 Filtered<
304 tracing_subscriber::fmt::Layer<
305 Layered<
306 Filtered<
307 tracing_subscriber::fmt::Layer<
308 Registry,
309 DefaultFields,
310 tracing_subscriber::fmt::format::Format<
311 tracing_subscriber::fmt::format::Full,
312 (),
313 >,
314 >,
315 EnvFilter,
316 Registry,
317 >,
318 Registry,
319 >,
320 DefaultFields,
321 tracing_subscriber::fmt::format::Format<
322 tracing_subscriber::fmt::format::Full,
323 (),
324 >,
325 RollingFileAppender,
326 >,
327 EnvFilter,
328 Layered<
329 Filtered<
330 tracing_subscriber::fmt::Layer<
331 Registry,
332 DefaultFields,
333 tracing_subscriber::fmt::format::Format<
334 tracing_subscriber::fmt::format::Full,
335 (),
336 >,
337 >,
338 EnvFilter,
339 Registry,
340 >,
341 Registry,
342 >,
343 >,
344 Layered<
345 Filtered<
346 tracing_subscriber::fmt::Layer<
347 Registry,
348 DefaultFields,
349 tracing_subscriber::fmt::format::Format<
350 tracing_subscriber::fmt::format::Full,
351 (),
352 >,
353 >,
354 EnvFilter,
355 Registry,
356 >,
357 Registry,
358 >,
359 >,
360 >,
361 Layered<
362 Filtered<
363 tracing_subscriber::fmt::Layer<
364 Layered<
365 Filtered<
366 tracing_subscriber::fmt::Layer<
367 Registry,
368 DefaultFields,
369 tracing_subscriber::fmt::format::Format<
370 tracing_subscriber::fmt::format::Full,
371 (),
372 >,
373 >,
374 EnvFilter,
375 Registry,
376 >,
377 Registry,
378 >,
379 DefaultFields,
380 tracing_subscriber::fmt::format::Format<tracing_subscriber::fmt::format::Full, ()>,
381 RollingFileAppender,
382 >,
383 EnvFilter,
384 Layered<
385 Filtered<
386 tracing_subscriber::fmt::Layer<
387 Registry,
388 DefaultFields,
389 tracing_subscriber::fmt::format::Format<
390 tracing_subscriber::fmt::format::Full,
391 (),
392 >,
393 >,
394 EnvFilter,
395 Registry,
396 >,
397 Registry,
398 >,
399 >,
400 Layered<
401 Filtered<
402 tracing_subscriber::fmt::Layer<
403 Registry,
404 DefaultFields,
405 tracing_subscriber::fmt::format::Format<
406 tracing_subscriber::fmt::format::Full,
407 (),
408 >,
409 >,
410 EnvFilter,
411 Registry,
412 >,
413 Registry,
414 >,
415 >,
416>;
417pub(crate) fn set_up_tracing(widget_name: &str) -> HomeHandle {
418 let runtime_dir = std::env::var("XDG_RUNTIME_DIR").expect("runtime dir is not set");
419 let logging_dir = runtime_dir + "/spell/";
420 let socket_dir = logging_dir.clone() + "/spell.sock";
421 let _ = fs::create_dir(Path::new(&logging_dir));
424 let _ = fs::remove_file(&socket_dir);
425 let stream = UnixDatagram::unbound().unwrap();
428 stream
429 .set_nonblocking(true)
430 .expect("Non blocking couldn't be set");
431
432 let writer = RollingFileAppender::builder()
433 .rotation(Rotation::HOURLY) .filename_prefix(widget_name) .filename_suffix("log") .build(&logging_dir) .expect("initializing rolling file appender failed");
438
439 let layer_writer = fmt::layer()
441 .without_time()
442 .with_target(false)
443 .with_writer(writer)
444 .with_filter(EnvFilter::new("spell_framework=trace,info"));
445
446 let layer_socket = fmt::Layer::default()
448 .without_time()
449 .with_target(false)
450 .with_writer(Mutex::new(SocketWriter::new(stream)))
451 .with_filter(EnvFilter::new("spell_framework=info, warn"));
452
453 let (layer_env, handle) = LoadLayer::new(layer_socket);
454 let subs = tracing_subscriber::registry()
455 .with(
457 fmt::layer()
458 .without_time()
459 .with_target(false)
460 .with_filter(EnvFilter::new("spell_framework=info, warn")),
461 )
462 .with(layer_writer)
464 .with(layer_env);
466 let _ = tracing::subscriber::set_global_default(subs);
467 handle
468}
469
470pub(crate) struct SocketWriter {
471 socket: UnixDatagram,
472 }
474
475impl SocketWriter {
476 fn new(socket: UnixDatagram) -> Self {
477 SocketWriter { socket }
478 }
479}
480
481impl Write for SocketWriter {
482 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
483 let runtime_dir = std::env::var("XDG_RUNTIME_DIR").expect("runtime dir is not set");
484 let logging_dir = runtime_dir + "/spell/";
485 let socket_dir = logging_dir.clone() + "/spell.sock";
486
487 self.socket.send_to(buf, Path::new(&socket_dir))
488 }
489
490 fn flush(&mut self) -> std::io::Result<()> {
491 Ok(())
492 }
493}
494
495#[allow(dead_code)]
498pub enum LayerConf {
499 Window(WindowConf),
500 Windows(Vec<WindowConf>),
501 Lock(u32, u32),
502}