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