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_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/// Unused Internal struct representation of a pixel, it is similar to slint's
15/// representation of [pixel]() but implement few more trait. Currently, redundent
16#[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        // self.a = a as u8;
35        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        // TODO This needs to be decided to see how it should be 0xFF or 0x00;
49        // I think there is a bug in slint which is causing the leak of This
50        // value.
51        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/// WindowConf is an essential struct passed on to widget constructor functions (like invoke_spell
64/// of generated code) for defining the specifications of the widget.
65///
66/// ## Panics
67///
68/// event loops like [cast_spell](crate::cast_spell) will panic if 0 is provided as width or height.
69#[derive(Debug, Clone)]
70pub struct WindowConf {
71    /// Defines the widget width in pixels. On setting values greater than the provided pixels of
72    /// monitor, the widget offsets from monitor's rectangular monitor space. It is important to
73    /// note that the value should be the maximum width the widget will ever attain, not the
74    /// current width in case of resizeable widgets. This value has no default and needs to be set.
75    pub width: u32,
76    /// Defines the widget height in pixels. On setting values greater than the provided pixels of
77    /// monitor, the widget offsets from monitor's rectangular monitor space. It is important to
78    /// note that the value should be the maximum height the widget will ever attain, not the
79    /// current height in case of resizeable widgets. This value has no default and needs to be set.
80    pub height: u32,
81    /// Defines the Anchors to which the window needs to be attached. View [`Anchor`] for
82    /// related explaination of usage. If both values are None, then widget is displayed in the
83    /// center of screen.
84    pub anchor: (Option<Anchor>, Option<Anchor>),
85    /// Defines the margin of widget from monitor edges, negative values make the widget go outside
86    /// of monitor pixels if anchored to some edge(s). Otherwise, the widget moves to the opposite
87    /// direction to the given pixels. Defaults to `0` for all sides.
88    pub margin: (i32, i32, i32, i32),
89    /// Defines the possible layer on which to define the widget. View [`Layer`] for more details.
90    /// Defaults to [`Layer::Top`].
91    pub layer_type: Layer,
92    /// Defines the relation of widget with Keyboard. View [`KeyboardInteractivity`] for more
93    /// details. Defauts to [`KeyboardInteractivity::None`]
94    pub board_interactivity: Cell<KeyboardInteractivity>,
95    /// Defines if the widget is exclusive of not,if not set to None, else set to number of pixels to
96    /// set as exclusive zone as i32. Defaults to no exclusive zone.
97    pub exclusive_zone: Option<i32>,
98    /// Defines the monitor name on which to spawn the window.
99    /// When no monitor is provided, the window is spawned on the default monitor.
100    pub monitor_name: Option<String>,
101    /// Defines if the method of scrolling for the widget should be natural or
102    /// reverse. Defaults to reverse scrolling. Learn more about scrolling types
103    /// [here](https://blog.logrocket.com/ux-design/natural-vs-reverse-scrolling/).
104    pub natural_scroll: bool,
105}
106
107impl WindowConf {
108    /// constructor method for initialising an instance of WindowConf.
109    #[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    /// Creates a builder instance for creation of WindowConf, to view defaults
136    /// head over to documentation of [`WindowConf`]'s parameters.
137    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    /// Sets [`WindowConf::width`].
157    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    /// Sets [`WindowConf::height`].
164    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    /// Sets first anchor of [`WindowConf::anchor`].
171    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    /// Sets second anchor of [`WindowConf::anchor`].
178    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    /// Sets [`WindowConf::margin`].
185    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    /// Sets [`WindowConf::layer_type`].
192    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    /// Sets [`WindowConf::board_interactivity`].
199    pub fn board_interactivity(&mut self, board: KeyboardInteractivity) -> &mut Self {
200        let x = self;
201        x.board_interactivity = board;
202        x
203    }
204
205    /// Sets [`WindowConf::exclusive_zone`].
206    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    /// Sets [`WindowConf::monitor_name`].
213    pub fn monitor(&mut self, name: String) -> &mut Self {
214        let x = self;
215        x.monitor_name = Some(name);
216        x
217    }
218
219    /// Sets [`WindowConf::natural_scroll`].
220    pub fn natural_scroll(&mut self, scroll: bool) -> &mut Self {
221        let x = self;
222        x.natural_scroll = scroll;
223        x
224    }
225
226    /// Creates an instnce of [`WindowConf`] with the provided configurations.
227    /// This function result in an error if width and height are not set or they
228    /// are set to zero.
229    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 socket_cli_dir = logging_dir.clone() + "/spell_cli";
441
442    let _ = fs::create_dir(Path::new(&logging_dir));
443    let _ = fs::remove_file(&socket_dir);
444    // let _ = fs::File::create(&socket_cli_dir);
445
446    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) // rotate log files once every hour
453        .filename_prefix(widget_name) // log file names will be prefixed with `myapp.`
454        .filename_suffix("log") // log file names will be suffixed with `.log`
455        .build(&logging_dir) // try to build an appender that stores log files in `/var/log`
456        .expect("initializing rolling file appender failed");
457
458    // Logs to be stored in case of debugging.
459    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    // Logs on socket read by cli.
466    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        // Logs shown in stdout when program runs.
475        .with(
476            fmt::layer()
477                .without_time()
478                .with_target(false)
479                .with_filter(EnvFilter::new("spell_framework=info, warn")),
480        )
481        // Logs for file.
482        .with(layer_writer)
483        // Logs for cli
484        .with(layer_env);
485    let _ = tracing::subscriber::set_global_default(subs);
486    handle
487}
488
489pub(crate) struct SocketWriter {
490    socket: UnixDatagram,
491    // formatter: Format<DefaultFields>,
492}
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// TODO this will be made public when multiple widgets in the same layer is supported.
515// Likely it will be easy after the resize action is implemented
516#[allow(dead_code)]
517pub enum LayerConf {
518    Window(WindowConf),
519    Windows(Vec<WindowConf>),
520    Lock(u32, u32),
521}