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::{level_filters::LevelFilter, warn};
5use tracing_appender::rolling::{RollingFileAppender, Rotation};
6use tracing_subscriber::{
7 EnvFilter, Layer as TracingTraitLayer,
8 fmt::{
9 self, Layer as TracingLayer,
10 format::{DefaultFields, Format, Full},
11 },
12 layer::{Layered, SubscriberExt},
13 registry::Registry,
14 reload::{Handle, Layer as LoadLayer},
15};
16
17#[allow(dead_code)]
20#[derive(Default)]
21pub struct Rgba8Pixel {
22 pub a: u8,
23 pub r: u8,
24 pub g: u8,
25 pub b: u8,
26}
27
28impl Rgba8Pixel {
29 pub fn new(a: u8, r: u8, g: u8, b: u8) -> Self {
30 Rgba8Pixel { a, r, g, b }
31 }
32}
33
34impl TargetPixel for Rgba8Pixel {
35 fn blend(&mut self, color: slint::platform::software_renderer::PremultipliedRgbaColor) {
36 let a: u16 = (u8::MAX - color.alpha) as u16;
37 let out_a = color.alpha as u16 + (self.a as u16 * (255 - color.alpha) as u16) / 255;
39 self.a = out_a as u8;
40 self.r = (self.r as u16 * a / 255) as u8 + color.red;
41 self.g = (self.g as u16 * a / 255) as u8 + color.green;
42 self.b = (self.b as u16 * a / 255) as u8 + color.blue;
43 }
44
45 fn from_rgb(red: u8, green: u8, blue: u8) -> Self {
46 let a = 0xFF;
47 Self::new(a, red, green, blue)
48 }
49
50 fn background() -> Self {
51 let a: u8 = 0x00;
55 Self::new(a, 0, 0, 0)
56 }
57}
58
59impl std::marker::Copy for Rgba8Pixel {}
60impl std::clone::Clone for Rgba8Pixel {
61 fn clone(&self) -> Self {
62 *self
63 }
64}
65
66#[derive(Debug, Clone)]
73pub struct WindowConf {
74 pub width: u32,
79 pub height: u32,
84 pub anchor: (Option<Anchor>, Option<Anchor>),
88 pub margin: (i32, i32, i32, i32),
92 pub layer_type: Layer,
94 pub board_interactivity: Cell<KeyboardInteractivity>,
97 pub exclusive_zone: Option<i32>,
100}
101
102impl WindowConf {
103 #[allow(clippy::too_many_arguments)]
105 pub fn new(
106 max_width: u32,
107 max_height: u32,
108 anchor: (Option<Anchor>, Option<Anchor>),
109 margin: (i32, i32, i32, i32),
110 layer_type: Layer,
111 board_interactivity: KeyboardInteractivity,
112 exclusive_zone: Option<i32>,
113 ) -> Self {
114 WindowConf {
115 width: max_width,
116 height: max_height,
117 anchor,
118 margin,
119 layer_type,
120 board_interactivity: Cell::new(board_interactivity),
121 exclusive_zone,
122 }
123 }
124}
125
126pub(crate) type HomeHandle =
127 Handle<EnvFilter, Layered<TracingLayer<Registry, DefaultFields, Format<Full, ()>>, Registry>>;
128pub(crate) fn set_up_tracing(widget_name: &str) -> HomeHandle {
129 let runtime_dir = std::env::var("XDG_RUNTIME_DIR").expect("runtime dir is not set");
130 let logging_dir = runtime_dir + "/spell/";
131 let socket_dir = logging_dir.clone() + "/spell.sock";
132 let socket_cli_dir = logging_dir.clone() + "/spell_cli";
133
134 let _ = fs::create_dir(Path::new(&logging_dir));
135 let _ = fs::remove_file(&socket_dir);
136 let _ = fs::File::create(&socket_cli_dir);
137
138 let stream = UnixDatagram::unbound().unwrap();
139 stream
140 .set_nonblocking(true)
141 .expect("Non blocking couldn't be set");
142
143 let writer = RollingFileAppender::builder()
144 .rotation(Rotation::HOURLY) .filename_prefix(widget_name) .filename_suffix("log") .build(&logging_dir) .expect("initializing rolling file appender failed");
149
150 let layer_writer = fmt::layer()
151 .without_time()
152 .with_target(false)
153 .with_writer(writer)
154 .with_filter(EnvFilter::new("spell_framework=trace,info"));
155
156 let (layer_env, handle) = LoadLayer::new(
163 EnvFilter::from_default_env().add_directive(
164 "spell-framework=info,warn"
165 .parse()
166 .unwrap_or(LevelFilter::INFO.into()),
167 ),
168 );
169 let layer_socket = fmt::layer().with_writer(Mutex::new(SocketWriter::new(stream)));
170 let subs = tracing_subscriber::registry()
171 .with(fmt::layer().without_time().with_target(false))
172 .with(layer_env)
173 .with(layer_socket)
174 .with(layer_writer);
175 let _ = tracing::subscriber::set_global_default(subs);
177 handle
178}
179
180pub(crate) struct SocketWriter {
181 socket: UnixDatagram,
182 }
184
185impl SocketWriter {
186 fn new(socket: UnixDatagram) -> Self {
187 SocketWriter { socket }
188 }
189}
190
191impl Write for SocketWriter {
192 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
193 let runtime_dir = std::env::var("XDG_RUNTIME_DIR").expect("runtime dir is not set");
194 let logging_dir = runtime_dir + "/spell/";
195 let socket_dir = logging_dir.clone() + "/spell.sock";
196
197 self.socket.send_to(buf, Path::new(&socket_dir))
198 }
199
200 fn flush(&mut self) -> std::io::Result<()> {
201 Ok(())
202 }
203}
204
205#[allow(dead_code)]
208pub enum LayerConf {
209 Window(WindowConf),
210 Windows(Vec<WindowConf>),
211 Lock(u32, u32),
212}