Skip to main content

spell_framework/
configure.rs

1use smithay_client_toolkit::shell::wlr_layer::{Anchor, KeyboardInteractivity, Layer};
2use std::{cell::Cell, fs, io::Write, os::unix::net::UnixDatagram, path::Path, sync::Mutex};
3use tracing_appender::rolling::{RollingFileAppender, Rotation};
4use tracing_subscriber::{
5    EnvFilter, Layer as TracingTraitLayer,
6    filter::Filtered,
7    fmt::{self, format::DefaultFields},
8    layer::{Layered, SubscriberExt},
9    registry::Registry,
10    reload::Layer as LoadLayer,
11};
12
13/// WindowConf is an essential struct passed on to widget constructor functions (like invoke_spell
14/// of generated code) for defining the specifications of the widget.
15///
16/// ## Panics
17///
18/// event loops like [cast_spell](crate::cast_spell) will panic if 0 is provided as width or height.
19#[derive(Debug, Clone)]
20pub struct WindowConf {
21    /// Defines the widget width in pixels. On setting values greater than the provided pixels of
22    /// monitor, the widget offsets from monitor's rectangular monitor space. It is important to
23    /// note that the value should be the maximum width the widget will ever attain, not the
24    /// current width in case of resizeable widgets. This value has no default and needs to be set.
25    pub width: u32,
26    /// Defines the widget height in pixels. On setting values greater than the provided pixels of
27    /// monitor, the widget offsets from monitor's rectangular monitor space. It is important to
28    /// note that the value should be the maximum height the widget will ever attain, not the
29    /// current height in case of resizeable widgets. This value has no default and needs to be set.
30    pub height: u32,
31    /// Defines the Anchors to which the window needs to be attached. View [`Anchor`] for
32    /// related explaination of usage. If both values are None, then widget is displayed in the
33    /// center of screen.
34    pub anchor: [Option<Anchor>; 4],
35    /// Defines the margin of widget from monitor edges, negative values make the widget go outside
36    /// of monitor pixels if anchored to some edge(s). Otherwise, the widget moves to the opposite
37    /// direction to the given pixels. Defaults to `0` for all sides.
38    pub margin: (i32, i32, i32, i32),
39    /// Defines the possible layer on which to define the widget. View [`Layer`] for more details.
40    /// Defaults to [`Layer::Top`].
41    pub layer_type: Layer,
42    /// Defines the relation of widget with Keyboard. View [`KeyboardInteractivity`] for more
43    /// details. Defauts to [`KeyboardInteractivity::None`]
44    pub board_interactivity: Cell<KeyboardInteractivity>,
45    /// Defines if the widget is exclusive of not,if not set to None, else set to number of pixels to
46    /// set as exclusive zone as i32. Defaults to no exclusive zone.
47    pub exclusive_zone: Option<i32>,
48    /// Defines the monitor name on which to spawn the window.
49    /// When no monitor is provided, the window is spawned on the default monitor.
50    pub monitor_name: Option<String>,
51    /// Defines if the method of scrolling for the widget should be natural or
52    /// reverse. Defaults to reverse scrolling. Learn more about scrolling types
53    /// [here](https://blog.logrocket.com/ux-design/natural-vs-reverse-scrolling/).
54    pub natural_scroll: bool,
55}
56
57impl WindowConf {
58    /// Creates a builder instance for creation of WindowConf, to view defaults
59    /// head over to documentation of [`WindowConf`]'s parameters.
60    pub fn builder() -> WindowConfBuilder {
61        WindowConfBuilder::default()
62    }
63}
64
65/// A builder method for [`WindowConf`]. For default values, refer to parameters
66/// of [`WindowConf`].
67#[derive(Default)]
68pub struct WindowConfBuilder {
69    max_width: u32,
70    max_height: u32,
71    anchor: [Option<Anchor>; 4],
72    margin: (i32, i32, i32, i32),
73    layer_type: Option<Layer>,
74    board_interactivity: KeyboardInteractivity,
75    exclusive_zone: Option<i32>,
76    monitor_name: Option<String>,
77    natural_scroll: bool,
78}
79
80impl WindowConfBuilder {
81    /// Sets [`WindowConf::width`].
82    pub fn width<I: Into<u32>>(&mut self, width: I) -> &mut Self {
83        let new = self;
84        new.max_width = width.into();
85        new
86    }
87
88    /// Sets [`WindowConf::height`].
89    pub fn height<I: Into<u32>>(&mut self, height: I) -> &mut Self {
90        let x = self;
91        x.max_height = height.into();
92        x
93    }
94
95    /// Sets first anchor of [`WindowConf::anchor`].
96    pub fn anchor_1(&mut self, anchor: Anchor) -> &mut Self {
97        let x = self;
98        x.anchor[0] = Some(anchor);
99        x
100    }
101
102    /// Sets second anchor of [`WindowConf::anchor`].
103    pub fn anchor_2(&mut self, anchor: Anchor) -> &mut Self {
104        let x = self;
105        x.anchor[1] = Some(anchor);
106        x
107    }
108
109    /// Sets third anchor of [`WindowConf::anchor`].
110    pub fn anchor_3(&mut self, anchor: Anchor) -> &mut Self {
111        let x = self;
112        x.anchor[2] = Some(anchor);
113        x
114    }
115
116    /// Sets fourth anchor of [`WindowConf::anchor`].
117    pub fn anchor_4(&mut self, anchor: Anchor) -> &mut Self {
118        let x = self;
119        x.anchor[3] = Some(anchor);
120        x
121    }
122
123    /// Sets [`WindowConf::margin`].
124    pub fn margins(&mut self, top: i32, right: i32, bottom: i32, left: i32) -> &mut Self {
125        let x = self;
126        x.margin = (top, right, bottom, left);
127        x
128    }
129
130    /// Sets [`WindowConf::layer_type`].
131    pub fn layer_type(&mut self, layer: Layer) -> &mut Self {
132        let x = self;
133        x.layer_type = Some(layer);
134        x
135    }
136
137    /// Sets [`WindowConf::board_interactivity`].
138    pub fn board_interactivity(&mut self, board: KeyboardInteractivity) -> &mut Self {
139        let x = self;
140        x.board_interactivity = board;
141        x
142    }
143
144    /// Sets [`WindowConf::exclusive_zone`].
145    pub fn exclusive_zone(&mut self, dimention: i32) -> &mut Self {
146        let x = self;
147        x.exclusive_zone = Some(dimention);
148        x
149    }
150
151    /// Sets [`WindowConf::monitor_name`].
152    pub fn monitor(&mut self, name: String) -> &mut Self {
153        let x = self;
154        x.monitor_name = Some(name);
155        x
156    }
157
158    /// Sets [`WindowConf::natural_scroll`].
159    pub fn natural_scroll(&mut self, scroll: bool) -> &mut Self {
160        let x = self;
161        x.natural_scroll = scroll;
162        x
163    }
164
165    /// Creates an instnce of [`WindowConf`] with the provided configurations.
166    /// This function result in an error if width and height are not set or they
167    /// are set to zero.
168    pub fn build(&self) -> Result<WindowConf, Box<dyn std::error::Error>> {
169        Ok(WindowConf {
170            width: if self.max_width != 0 {
171                self.max_width
172            } else {
173                return Err("width is either not defined or set to zero".into());
174            },
175            height: if self.max_height != 0 {
176                self.max_height
177            } else {
178                return Err("height is either not defined or set to zero".into());
179            },
180            anchor: self.anchor,
181            margin: self.margin,
182            layer_type: match self.layer_type {
183                None => Layer::Top,
184                Some(val) => val,
185            },
186            board_interactivity: Cell::new(self.board_interactivity),
187            exclusive_zone: self.exclusive_zone,
188            monitor_name: self.monitor_name.clone(),
189            natural_scroll: self.natural_scroll,
190        })
191    }
192}
193
194pub(crate) type HomeHandle = tracing_subscriber::reload::Handle<
195    Filtered<
196        tracing_subscriber::fmt::Layer<
197            Layered<
198                Filtered<
199                    tracing_subscriber::fmt::Layer<
200                        Layered<
201                            Filtered<
202                                tracing_subscriber::fmt::Layer<
203                                    Registry,
204                                    DefaultFields,
205                                    tracing_subscriber::fmt::format::Format<
206                                        tracing_subscriber::fmt::format::Full,
207                                        (),
208                                    >,
209                                >,
210                                EnvFilter,
211                                Registry,
212                            >,
213                            Registry,
214                        >,
215                        DefaultFields,
216                        tracing_subscriber::fmt::format::Format<
217                            tracing_subscriber::fmt::format::Full,
218                            (),
219                        >,
220                        RollingFileAppender,
221                    >,
222                    EnvFilter,
223                    Layered<
224                        Filtered<
225                            tracing_subscriber::fmt::Layer<
226                                Registry,
227                                DefaultFields,
228                                tracing_subscriber::fmt::format::Format<
229                                    tracing_subscriber::fmt::format::Full,
230                                    (),
231                                >,
232                            >,
233                            EnvFilter,
234                            Registry,
235                        >,
236                        Registry,
237                    >,
238                >,
239                Layered<
240                    Filtered<
241                        tracing_subscriber::fmt::Layer<
242                            Registry,
243                            DefaultFields,
244                            tracing_subscriber::fmt::format::Format<
245                                tracing_subscriber::fmt::format::Full,
246                                (),
247                            >,
248                        >,
249                        EnvFilter,
250                        Registry,
251                    >,
252                    Registry,
253                >,
254            >,
255            DefaultFields,
256            tracing_subscriber::fmt::format::Format<tracing_subscriber::fmt::format::Full, ()>,
257            std::sync::Mutex<SocketWriter>,
258        >,
259        EnvFilter,
260        Layered<
261            Filtered<
262                tracing_subscriber::fmt::Layer<
263                    Layered<
264                        Filtered<
265                            tracing_subscriber::fmt::Layer<
266                                Registry,
267                                DefaultFields,
268                                tracing_subscriber::fmt::format::Format<
269                                    tracing_subscriber::fmt::format::Full,
270                                    (),
271                                >,
272                            >,
273                            EnvFilter,
274                            Registry,
275                        >,
276                        Registry,
277                    >,
278                    DefaultFields,
279                    tracing_subscriber::fmt::format::Format<
280                        tracing_subscriber::fmt::format::Full,
281                        (),
282                    >,
283                    RollingFileAppender,
284                >,
285                EnvFilter,
286                Layered<
287                    Filtered<
288                        tracing_subscriber::fmt::Layer<
289                            Registry,
290                            DefaultFields,
291                            tracing_subscriber::fmt::format::Format<
292                                tracing_subscriber::fmt::format::Full,
293                                (),
294                            >,
295                        >,
296                        EnvFilter,
297                        Registry,
298                    >,
299                    Registry,
300                >,
301            >,
302            Layered<
303                Filtered<
304                    tracing_subscriber::fmt::Layer<
305                        Registry,
306                        DefaultFields,
307                        tracing_subscriber::fmt::format::Format<
308                            tracing_subscriber::fmt::format::Full,
309                            (),
310                        >,
311                    >,
312                    EnvFilter,
313                    Registry,
314                >,
315                Registry,
316            >,
317        >,
318    >,
319    Layered<
320        Filtered<
321            tracing_subscriber::fmt::Layer<
322                Layered<
323                    Filtered<
324                        tracing_subscriber::fmt::Layer<
325                            Registry,
326                            DefaultFields,
327                            tracing_subscriber::fmt::format::Format<
328                                tracing_subscriber::fmt::format::Full,
329                                (),
330                            >,
331                        >,
332                        EnvFilter,
333                        Registry,
334                    >,
335                    Registry,
336                >,
337                DefaultFields,
338                tracing_subscriber::fmt::format::Format<tracing_subscriber::fmt::format::Full, ()>,
339                RollingFileAppender,
340            >,
341            EnvFilter,
342            Layered<
343                Filtered<
344                    tracing_subscriber::fmt::Layer<
345                        Registry,
346                        DefaultFields,
347                        tracing_subscriber::fmt::format::Format<
348                            tracing_subscriber::fmt::format::Full,
349                            (),
350                        >,
351                    >,
352                    EnvFilter,
353                    Registry,
354                >,
355                Registry,
356            >,
357        >,
358        Layered<
359            Filtered<
360                tracing_subscriber::fmt::Layer<
361                    Registry,
362                    DefaultFields,
363                    tracing_subscriber::fmt::format::Format<
364                        tracing_subscriber::fmt::format::Full,
365                        (),
366                    >,
367                >,
368                EnvFilter,
369                Registry,
370            >,
371            Registry,
372        >,
373    >,
374>;
375pub(crate) fn set_up_tracing(widget_name: &str) -> HomeHandle {
376    let runtime_dir = std::env::var("XDG_RUNTIME_DIR").expect("runtime dir is not set");
377    let logging_dir = runtime_dir + "/spell/";
378    let socket_dir = logging_dir.clone() + "/spell.sock";
379    // let socket_cli_dir = logging_dir.clone() + "/spell_cli";
380
381    let _ = fs::create_dir(Path::new(&logging_dir));
382    let _ = fs::remove_file(&socket_dir);
383    // let _ = fs::File::create(&socket_cli_dir);
384
385    let stream = UnixDatagram::unbound().unwrap();
386    stream
387        .set_nonblocking(true)
388        .expect("Non blocking couldn't be set");
389
390    let writer = RollingFileAppender::builder()
391        .rotation(Rotation::HOURLY) // rotate log files once every hour
392        .filename_prefix(widget_name) // log file names will be prefixed with `myapp.`
393        .filename_suffix("log") // log file names will be suffixed with `.log`
394        .build(&logging_dir) // try to build an appender that stores log files in `/var/log`
395        .expect("initializing rolling file appender failed");
396
397    // Logs to be stored in case of debugging.
398    let layer_writer = fmt::layer()
399        .without_time()
400        .with_target(false)
401        .with_writer(writer)
402        .with_filter(EnvFilter::new("spell_framework=trace,info"));
403
404    // Logs on socket read by cli.
405    let layer_socket = fmt::Layer::default()
406        .without_time()
407        .with_target(false)
408        .with_writer(Mutex::new(SocketWriter::new(stream)))
409        .with_filter(EnvFilter::new("spell_framework=info, warn"));
410
411    let (layer_env, handle) = LoadLayer::new(layer_socket);
412    let subs = tracing_subscriber::registry()
413        // Logs shown in stdout when program runs.
414        .with(
415            fmt::layer()
416                .without_time()
417                .with_target(false)
418                .with_filter(EnvFilter::new("spell_framework=info, warn")),
419        )
420        // Logs for file.
421        .with(layer_writer)
422        // Logs for cli
423        .with(layer_env);
424    let _ = tracing::subscriber::set_global_default(subs);
425    handle
426}
427
428pub(crate) struct SocketWriter {
429    socket: UnixDatagram,
430    // formatter: Format<DefaultFields>,
431}
432
433impl SocketWriter {
434    fn new(socket: UnixDatagram) -> Self {
435        SocketWriter { socket }
436    }
437}
438
439impl Write for SocketWriter {
440    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
441        let runtime_dir = std::env::var("XDG_RUNTIME_DIR").expect("runtime dir is not set");
442        let logging_dir = runtime_dir + "/spell/";
443        let socket_dir = logging_dir.clone() + "/spell.sock";
444
445        self.socket.send_to(buf, Path::new(&socket_dir))
446    }
447
448    fn flush(&mut self) -> std::io::Result<()> {
449        Ok(())
450    }
451}
452
453// TODO this will be made public when multiple widgets in the same layer is supported.
454// Likely it will be easy after the resize action is implemented
455#[allow(dead_code)]
456pub enum LayerConf {
457    Window(WindowConf),
458    Windows(Vec<WindowConf>),
459    Lock(u32, u32),
460}