Skip to main content

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::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/// Unused Internal struct representation of a pixel, it is similar to slint's
16/// representation of [pixel]() but implement few more trait. Currently, redundent
17#[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        // self.a = a as u8;
36        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        // TODO This needs to be decided to see how it should be 0xFF or 0x00;
50        // I think there is a bug in slint which is causing the leak of This
51        // value.
52        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/// WindowConf is an essential struct passed on to widget constructor functions (like [invoke_spell](crate::wayland_adapter::SpellWin::invoke_spell))
65/// for defining the specifications of the widget.
66///
67/// ## Panics
68///
69/// event loops like [cast_spell](crate::cast_spell) and [encahnt_spells](crate::enchant_spells) will panic if 0 is provided as width or height.
70#[derive(Debug, Clone)]
71pub struct WindowConf {
72    /// Defines the widget width in pixels. On setting values greater than the provided pixels of
73    /// monitor, the widget offsets from monitor's rectangular monitor space. It is important to
74    /// note that the value should be the maximum width the widget will ever attain, not the
75    /// current width in case of resizeable widgets. This value has no default and needs to be set.
76    pub width: u32,
77    /// Defines the widget height in pixels. On setting values greater than the provided pixels of
78    /// monitor, the widget offsets from monitor's rectangular monitor space. It is important to
79    /// note that the value should be the maximum height the widget will ever attain, not the
80    /// current height in case of resizeable widgets. This value has no default and needs to be set.
81    pub height: u32,
82    /// Defines the Anchors to which the window needs to be attached. View [`Anchor`] for
83    /// related explaination of usage. If both values are None, then widget is displayed in the
84    /// center of screen.
85    pub anchor: (Option<Anchor>, Option<Anchor>),
86    /// Defines the margin of widget from monitor edges, negative values make the widget go outside
87    /// of monitor pixels if anchored to some edge(s). Otherwise, the widget moves to the opposite
88    /// direction to the given pixels. Defaults to `0` for all sides.
89    pub margin: (i32, i32, i32, i32),
90    /// Defines the possible layer on which to define the widget. View [`Layer`] for more details.
91    /// Defaults to [`Layer::Top`].
92    pub layer_type: Layer,
93    /// Defines the relation of widget with Keyboard. View [`KeyboardInteractivity`] for more
94    /// details. Defauts to [`KeyboardInteractivity::None`]
95    pub board_interactivity: Cell<KeyboardInteractivity>,
96    /// Defines if the widget is exclusive of not,if not set to None, else set to number of pixels to
97    /// set as exclusive zone as i32. Defaults to no exclusive zone.
98    pub exclusive_zone: Option<i32>,
99    /// Defines the monitor name on which to spawn the window.
100    /// When no monitor is provided, the window is spawned on the default monitor.
101    pub monitor_name: Option<String>,
102    /// Defines if the method of scrolling for the widget should be natural or
103    /// reverse. Defaults to reverse scrolling. Learn more about scrolling types
104    /// [here](https://blog.logrocket.com/ux-design/natural-vs-reverse-scrolling/).
105    pub natural_scroll: bool,
106}
107
108impl WindowConf {
109    /// constructor method for initialising an instance of WindowConf.
110    #[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    /// Creates a builder instance for creation of WindowConf, to view defaults
137    /// head over to documentation of [`WindowConf`]'s parameters.
138    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    /// Sets [`WindowConf::width`].
158    pub fn width<I: Into<u32>>(&mut self, width: I) {
159        self.max_width = width.into();
160    }
161
162    /// Sets [`WindowConf::height`].
163    pub fn height<I: Into<u32>>(&mut self, height: I) {
164        self.max_width = height.into();
165    }
166
167    /// Sets first anchor of [`WindowConf::anchor`].
168    pub fn anchor_1(&mut self, anchor: Anchor) {
169        self.anchor.0 = Some(anchor);
170    }
171
172    /// Sets second anchor of [`WindowConf::anchor`].
173    pub fn anchor_2(&mut self, anchor: Anchor) {
174        self.anchor.1 = Some(anchor);
175    }
176
177    /// Sets [`WindowConf::margin`].
178    pub fn margins(&mut self, top: i32, right: i32, bottom: i32, left: i32) {
179        self.margin = (top, right, bottom, left)
180    }
181
182    /// Sets [`WindowConf::layer_type`].
183    pub fn layer_type(&mut self, layer: Layer) {
184        self.layer_type = Some(layer);
185    }
186
187    /// Sets [`WindowConf::board_interactivity`].
188    pub fn board_interactivity(&mut self, board: KeyboardInteractivity) {
189        self.board_interactivity = board;
190    }
191
192    /// Sets [`WindowConf::exclusive_zone`].
193    pub fn exclusive_zone(&mut self, dimention: i32) {
194        self.exclusive_zone = Some(dimention);
195    }
196
197    /// Sets [`WindowConf::monitor_name`].
198    pub fn monitor(&mut self, name: String) {
199        self.monitor_name = Some(name)
200    }
201
202    /// Sets [`WindowConf::natural_scroll`].
203    pub fn natural_scroll(&mut self, scroll: bool) {
204        self.natural_scroll = scroll;
205    }
206
207    /// Creates an instnce of [`WindowConf`] with the provided configurations.
208    /// This function result in an error if width and height are not set or they
209    /// are set to zero.
210    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 socket_cli_dir = logging_dir.clone() + "/spell_cli";
422
423    let _ = fs::create_dir(Path::new(&logging_dir));
424    let _ = fs::remove_file(&socket_dir);
425    // let _ = fs::File::create(&socket_cli_dir);
426
427    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) // rotate log files once every hour
434        .filename_prefix(widget_name) // log file names will be prefixed with `myapp.`
435        .filename_suffix("log") // log file names will be suffixed with `.log`
436        .build(&logging_dir) // try to build an appender that stores log files in `/var/log`
437        .expect("initializing rolling file appender failed");
438
439    // Logs to be stored in case of debugging.
440    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    // Logs on socket read by cli.
447    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        // Logs shown in stdout when program runs.
456        .with(
457            fmt::layer()
458                .without_time()
459                .with_target(false)
460                .with_filter(EnvFilter::new("spell_framework=info, warn")),
461        )
462        // Logs for file.
463        .with(layer_writer)
464        // Logs for cli
465        .with(layer_env);
466    let _ = tracing::subscriber::set_global_default(subs);
467    handle
468}
469
470pub(crate) struct SocketWriter {
471    socket: UnixDatagram,
472    // formatter: Format<DefaultFields>,
473}
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// TODO this will be made public when multiple widgets in the same layer is supported.
496// Likely it will be easy after the resize action is implemented
497#[allow(dead_code)]
498pub enum LayerConf {
499    Window(WindowConf),
500    Windows(Vec<WindowConf>),
501    Lock(u32, u32),
502}