spell_framework/
configure.rs

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/// Unused Internal struct representation of a pixel, it is similar to slint's
18/// representation of [pixel]() but implement few more trait. Currently, redundent
19#[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        // self.a = a as u8;
38        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        // TODO This needs to be decided to see how it should be 0xFF or 0x00;
52        // I think there is a bug in slint which is causing the leak of This
53        // value.
54        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/// WindowConf is an essential struct passed on to widget constructor functions (like [invoke_spell](crate::wayland_adapter::SpellWin::invoke_spell))
67/// for defining the specifications of the widget.
68///
69/// ## Panics
70///
71/// event loops like [cast_spell](crate::cast_spell) and [encahnt_spells](crate::enchant_spells) will panic if 0 is provided as width or height.
72#[derive(Debug, Clone)]
73pub struct WindowConf {
74    /// Defines the widget width in pixels. On setting values greater than the provided pixels of
75    /// monitor, the widget offsets from monitor's rectangular monitor space. It is important to
76    /// note that the value should be the maximum width the widget will ever attain, not the
77    /// current width in case of resizeable widgets.
78    pub width: u32,
79    /// Defines the widget height in pixels. On setting values greater than the provided pixels of
80    /// monitor, the widget offsets from monitor's rectangular monitor space. It is important to
81    /// note that the value should be the maximum height the widget will ever attain, not the
82    /// current height in case of resizeable widgets.
83    pub height: u32,
84    /// Defines the Anchors to which the window needs to be attached. View [`Anchor`] for
85    /// related explaination of usage. If both values are None, then widget is displayed in the
86    /// center of screen.
87    pub anchor: (Option<Anchor>, Option<Anchor>),
88    /// Defines the margin of widget from monitor edges, negative values make the widget go outside
89    /// of monitor pixels if anchored to some edge(s). Otherwise, the widget moves to the opposite
90    /// direction to the given pixels.
91    pub margin: (i32, i32, i32, i32),
92    /// Defines the possible layer on which to define the widget. View [`Layer`] for more details.
93    pub layer_type: Layer,
94    /// Defines the relation of widget with Keyboard. View [`KeyboardInteractivity`] for more
95    /// details.
96    pub board_interactivity: Cell<KeyboardInteractivity>,
97    /// Defines if the widget is exclusive of not,if not set to None, else set to number of pixels to
98    /// set as exclusive zone as i32.
99    pub exclusive_zone: Option<i32>,
100}
101
102impl WindowConf {
103    /// constructor method for initialising an instance of WindowConf.
104    #[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) // rotate log files once every hour
145        .filename_prefix(widget_name) // log file names will be prefixed with `myapp.`
146        .filename_suffix("log") // log file names will be suffixed with `.log`
147        .build(&logging_dir) // try to build an appender that stores log files in `/var/log`
148        .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    // if let Ok(val) = std::env::var("RUST_LOG") {
157    //     println!("Rust log value: {}", val);
158    // } else {
159    //     println!("Val not set");
160    // }
161
162    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    // .with(layer_std);
176    let _ = tracing::subscriber::set_global_default(subs);
177    handle
178}
179
180pub(crate) struct SocketWriter {
181    socket: UnixDatagram,
182    // formatter: Format<DefaultFields>,
183}
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// TODO this will be made public when multiple widgets in the same layer is supported.
206// Likely it will be easy after the resize action is implemented
207#[allow(dead_code)]
208pub enum LayerConf {
209    Window(WindowConf),
210    Windows(Vec<WindowConf>),
211    Lock(u32, u32),
212}