Skip to main content

spell_framework/
lib.rs

1#![doc(
2    html_logo_url = "https://raw.githubusercontent.com/VimYoung/Spell/main/spell-framework/assets/spell_trans.png"
3)]
4#![doc(
5    html_favicon_url = "https://raw.githubusercontent.com/VimYoung/Spell/main/spell-framework/assets/spell_trans.ico"
6)]
7// #![doc(html_favicon_url = "https://github.com/VimYoung/Spell/blob/bb01ae94a365d237ebb0db1df1b6eb37aea25367/spell-framework/assets/Spell.png")]
8#![doc = include_str!("../docs/entry.md")]
9#[warn(missing_docs)]
10mod configure;
11mod dbus_window_state;
12#[cfg(docsrs)]
13mod dummy_skia_docs;
14pub mod forge;
15#[cfg(feature = "i-slint-renderer-skia")]
16// #[cfg(feature = "pam-client2")]
17#[cfg(not(docsrs))]
18#[doc(hidden)]
19mod skia_non_docs;
20pub mod slint_adapter;
21pub mod vault;
22pub mod wayland_adapter;
23/// It contains related enums and struct which are used to manage,
24/// define and update various properties of a widget(viz a viz layer). You can import necessary
25/// types from this module to implement relevant features. See docs of related objects for
26/// their overview.
27pub mod layer_properties {
28    pub use crate::{configure::WindowConf, dbus_window_state::DataType};
29    pub use smithay_client_toolkit::shell::wlr_layer::Anchor as LayerAnchor;
30    pub use smithay_client_toolkit::shell::wlr_layer::KeyboardInteractivity as BoardType;
31    pub use smithay_client_toolkit::shell::wlr_layer::Layer as LayerType;
32}
33use dbus_window_state::{DataType, InternalHandle, deploy_zbus_service};
34pub use paste;
35pub use smithay_client_toolkit;
36use smithay_client_toolkit::reexports::calloop::channel::{Channel, Event, channel};
37use std::{
38    any::Any,
39    error::Error,
40    sync::{Arc, RwLock},
41    time::Duration,
42};
43pub use tracing;
44use tracing::{Level, error, info, instrument, span, trace, warn};
45use wayland_adapter::SpellWin;
46use zbus::Error as BusError;
47
48/// This a boilerplate trait for connection with CLI, it will be replaced by a procedural
49/// macro in the future.
50/// In the mean time, this function is implemented on a struct you would define in
51/// your `.slint` file. Then state of widgets should be stored as single property
52/// of that data type rather than on individual values.
53///
54/// ## Example
55///
56/// ```slint
57/// // Wrong Method is you want to handle on-close and is-expanded locally.
58/// export component MyWindow inherits Window {
59///   in-out property <bool> on-close: false;
60///   in-out property <bool> is-expanded: true;
61///   Rectangle {
62///      // Other widgets will come here.
63///   }
64/// }
65///
66/// // Correct method
67/// export component MyWindow inherits Window {
68///   in-out property <MyWinState> state: {on-close: false, is-expanded: true};
69///   Rectangle {
70///      // Other widgets will come here.
71///   }
72/// }
73/// export struct MyWinState {
74///   on-close: bool,
75///   is-expanded: true,
76/// }
77/// ```
78#[deprecated(
79    since = "1.0.2",
80    note = "Use IpcController instead, which works directly on the window. It will be remoed in release 1.0.3."
81)]
82pub trait ForeignController: Send + Sync + std::fmt::Debug {
83    /// On calling `spell-cli -l layer_name look
84    /// var_name`, the cli calls `get_type` method of the trait with `var_name` as input.
85    fn get_type(&self, key: &str) -> DataType;
86    /// It is called on `spell-cli -l layer_name update key value`. `as_any` is for syncing the changes
87    /// internally for now and need not be implemented by the end user.
88    fn change_val(&mut self, key: &str, val: DataType);
89    /// It is a type needed internally, it's implementation should return `self` to
90    /// avoid undefined behaviour.
91    fn as_any(&self) -> &dyn Any;
92}
93
94pub trait IpcController {
95    /// On calling `spell-cli -l layer_name look
96    /// var_name`, the cli calls `get_type` method of the trait with `var_name` as input.
97    fn get_type(&self, key: &str) -> String;
98    /// It is called on `spell-cli -l layer_name update key value`. `as_any` is for syncing the changes
99    /// internally for now and need not be implemented by the end user.
100    fn change_val(&mut self, key: &str, val: &str);
101}
102
103#[deprecated(
104    since = "1.0.2",
105    note = "This type is no longer needed and will be removed in release 1.0.3."
106)]
107pub type State = Arc<RwLock<dyn ForeignController>>;
108type States = Vec<Option<Box<dyn FnMut(State)>>>;
109/// This is the event loop which is to be called when initialising multiple windows through
110/// a single `main` file. It is important to remember that Each value of these vectors corresponds
111/// to the number on which a widget is initialised. So, this function will panic if the length of
112/// vectors of various types mentioned here are not equal.For more information on checking the
113/// arguments, view [cast_spell].
114#[deprecated(since = "1.0.2", note = "Use cast_spell macro instead.")]
115pub fn enchant_spells(
116    mut waywindows: Vec<SpellWin>,
117    states: Vec<Option<State>>,
118    mut set_callbacks: States,
119) -> Result<(), Box<dyn Error>> {
120    if waywindows.len() == states.len() && waywindows.len() == set_callbacks.len() {
121        info!("Starting windows");
122        let spans: Vec<span::Span> = waywindows.iter().map(|win| win.span.clone()).collect();
123        let mut internal_recievers: Vec<Channel<InternalHandle>> = Vec::new();
124        states.iter().enumerate().for_each(|(index, state)| {
125            internal_recievers.push(helper_fn_for_deploy(
126                waywindows[index].layer_name.clone(),
127                state,
128                waywindows[index].span.clone(),
129            ));
130            trace!("{:?}", &waywindows[index]);
131        });
132        trace!("Grabbed Internal recievers");
133        states.into_iter().enumerate().for_each(|(index, state)| {
134            let _guard = spans[index].enter();
135            let set_call = set_callbacks.remove(0);
136            if let Some(mut callback) = set_call {
137                let event_loop = waywindows[index].event_loop.clone();
138                event_loop
139                    .borrow()
140                    .handle()
141                    .insert_source(
142                        internal_recievers.remove(0),
143                        move |event_msg, _, state_data| {
144                            match event_msg {
145                                Event::Msg(int_handle) => {
146                                    match int_handle {
147                                        InternalHandle::StateValChange((key, data_type)) => {
148                                            trace!("Internal variable change called");
149                                            //Glad I could think of this sub scope for RwLock.
150                                            {
151                                                let mut state_inst =
152                                                    state.as_ref().unwrap().write().unwrap();
153                                                state_inst.change_val(&key, data_type);
154                                            }
155                                            callback(state.as_ref().unwrap().clone());
156                                        }
157                                        InternalHandle::ShowWinAgain => {
158                                            trace!("Internal show Called");
159                                            state_data.show_again();
160                                        }
161                                        InternalHandle::HideWindow => {
162                                            trace!("Internal hide called");
163                                            state_data.hide();
164                                        }
165                                    }
166                                }
167                                // TODO have to handle it properly.
168                                Event::Closed => {
169                                    info!("Internal Channel closed");
170                                }
171                            }
172                        },
173                    )
174                    .unwrap();
175            }
176        });
177        trace!("Setting internal handles as events and calling event loop.");
178
179        loop {
180            for (index, waywindow) in waywindows.iter_mut().enumerate() {
181                spans[index].in_scope(|| -> Result<(), Box<dyn Error>> {
182                    let event_loop = waywindow.event_loop.clone();
183                    event_loop
184                        .borrow_mut()
185                        .dispatch(Duration::from_millis(1), waywindow)?;
186                    Ok(())
187                })?;
188            }
189        }
190    } else {
191        error!("Lengths are unequal");
192        panic!(
193            "The lengths of given vectors are not equal. \n Make sure that given vector lengths are equal"
194        );
195    }
196}
197
198/// This is the primary function used for starting the event loop after creating the widgets,
199/// setting values and initialising windows. Example of the use can be found [here](https://github.com/VimYoung/Young-Shell/tree/main/src/bin).
200/// The function takes in the following function arguments:-
201/// 1. Wayland side of widget corresponding to it's slint window.
202/// 2. A instance of struct implementing [ForeignController]. This will be wrapped in `Arc` and
203///    `RwLock` as it would be used across threads internally, if the widget is static in nature
204///    and doesn't need state that needs to be changed remotely via CLI. You can parse in None.
205/// 3. A callback which is called when a CLI command is invoked changing the value. The closure
206///    gets an updated value of your state struct. The common method is to take the updated value
207///    and replace your existing state with it to reflect back the changes in the slint code. If
208///    state is provided, then it is important for now to pass a callback corresponding to it too.
209///    You can use this callback for example.
210/// ```rust
211/// move |state_value| {
212///     let controller_val = state_value.read().unwrap();
213///     let val = controller_val
214///         .as_any()
215///         .downcast_ref::<State>()
216///         .unwrap()
217///         .clone();
218///     ui_clone.unwrap().set_state(val);
219/// }
220/// // here `ui_clone` is weak pointer to my slint window for setting back the `state` property.
221/// ```
222#[deprecated(
223    since = "1.0.2",
224    note = "Use macro cast_spell instead. It will be removed in release 1.0.3."
225)]
226pub fn cast_spell<S: SpellAssociated + std::fmt::Debug>(
227    mut waywindow: S,
228    state: Option<State>,
229    set_callback: Option<Box<dyn FnMut(State)>>,
230) -> Result<(), Box<dyn Error>> {
231    let span = waywindow.get_span();
232    let s = span.clone();
233    span.in_scope(|| {
234        trace!("{:?}", &waywindow);
235        waywindow.on_call(state, set_callback, s)
236    })
237}
238
239pub fn cast_spell_inner<S: SpellAssociated + std::fmt::Debug>(
240    mut waywindow: S,
241) -> Result<(), Box<dyn Error>> {
242    let span = waywindow.get_span();
243    let s = span.clone();
244    span.in_scope(|| {
245        trace!("{:?}", &waywindow);
246        waywindow.on_call(None, None, s)
247    })
248}
249
250#[instrument(skip(state))]
251fn helper_fn_for_deploy(
252    layer_name: String,
253    state: &Option<State>,
254    span_log: span::Span,
255) -> Channel<InternalHandle> {
256    let (tx, rx) = channel::<InternalHandle>();
257    if let Some(some_state) = state {
258        let state_clone = some_state.clone();
259        std::thread::spawn(move || {
260            span_log.in_scope(move || {
261                let span_bus = span!(Level::INFO, "Zbus Logs",);
262                let _guard = span_bus.enter();
263                let rt = tokio::runtime::Builder::new_current_thread()
264                    .enable_all()
265                    .build()
266                    .unwrap();
267                if let Err(error) = rt.block_on(async move {
268                    trace!("Started Zbus service in a thread");
269                    deploy_zbus_service(state_clone, tx, layer_name).await?;
270                    Ok::<_, BusError>(())
271                }) {
272                    error!("Zbus panicked with following error: {}", error);
273                    Err(error)
274                } else {
275                    Ok(())
276                }
277            })
278        });
279    }
280    rx
281}
282
283/// Internal function for running event loops, implemented by [SpellWin] and
284/// [SpellLock][`crate::wayland_adapter::SpellLock`].
285pub trait SpellAssociated {
286    fn on_call(
287        &mut self,
288        state: Option<State>,
289        set_callback: Option<Box<dyn FnMut(State)>>,
290        span_log: tracing::span::Span,
291    ) -> Result<(), Box<dyn Error>>;
292
293    fn get_span(&self) -> span::Span {
294        span!(Level::INFO, "unnamed-widget")
295    }
296}
297
298pub trait SpellAssociatedNew {
299    fn on_call(&mut self) -> Result<(), Box<dyn Error>>;
300
301    fn get_span(&self) -> span::Span {
302        span!(Level::INFO, "unnamed-widget")
303    }
304}
305
306pub fn cast_spells_new(
307    mut windows: Vec<Box<dyn SpellAssociatedNew>>,
308) -> Result<(), Box<dyn Error>> {
309    loop {
310        for win in windows.iter_mut() {
311            let span = win.get_span().clone();
312            let _gaurd = span.enter();
313            win.on_call()?;
314        }
315    }
316}
317
318#[macro_export]
319macro_rules! generate_widgets {
320    ($($slint_win:ty),+) => {
321        use $crate::wayland_adapter::WinHandle;
322        use $crate::tracing;
323
324        $crate::paste::paste! {
325            $(
326                struct [<$slint_win Spell>] {
327                    ui: $slint_win ,
328                    way: SpellWin,
329                }
330
331                impl std::fmt::Debug for [<$slint_win Spell>] {
332                    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
333                        f.debug_struct("Spell")
334                        .field("wayland_side:", &self.way) // Add fields by name
335                        .finish() // Finalize the struct formatting
336                    }
337                }
338
339                impl [<$slint_win Spell>] {
340                    pub fn invoke_spell(name: &str, window_conf: WindowConf) -> Self {
341                        let way_win = SpellWin::invoke_spell(name, window_conf);
342                        [<$slint_win Spell>] {
343                            ui: $slint_win::new().unwrap(),
344                            way: way_win
345                        }
346                    }
347                    /// Internally calls [`crate::wayland_adapter::SpellWin::hide`]
348                    pub fn hide(&self) {
349                        self.way.hide();
350                    }
351
352                    /// Internally calls [`crate::wayland_adapter::SpellWin::show_again`]
353                    pub fn show_again(&mut self) {
354                        self.way.show_again();
355                    }
356
357                    /// Internally calls [`crate::wayland_adapter::SpellWin::toggle`]
358                    pub fn toggle(&mut self) {
359                        self.way.toggle();
360                    }
361
362                    /// Internally calls [`crate::wayland_adapter::SpellWin::grab_focus`]
363                    pub fn grab_focus(&self) {
364                        self.way.grab_focus();
365                    }
366
367                    /// Internally calls [`crate::wayland_adapter::SpellWin::remove_focus`]
368                    pub fn remove_focus(&self) {
369                        self.way.remove_focus();
370                    }
371
372                    /// Internally calls [`crate::wayland_adapter::SpellWin::add_input_region`]
373                    pub fn add_input_region(&self, x: i32, y: i32, width: i32, height: i32) {
374                        self.way.add_input_region(x, y, width, height);
375                    }
376
377                    /// Internally calls [`crate::wayland_adapter::SpellWin::subtract_input_region`]
378                    pub fn subtract_input_region(&self, x: i32, y: i32, width: i32, height: i32) {
379                        self.way.subtract_input_region(x, y, width, height);
380                    }
381
382                    /// Internally calls [`crate::wayland_adapter::SpellWin::add_opaque_region`]
383                    pub fn add_opaque_region(&self, x: i32, y: i32, width: i32, height: i32) {
384                        self.way.add_opaque_region(x, y, width, height);
385                    }
386
387                    /// Internally calls [`crate::wayland_adapter::SpellWin::subtract_opaque_region`]
388                    pub fn subtract_opaque_region(&self, x: i32, y: i32, width: i32, height: i32) {
389                        self.way.subtract_opaque_region(x, y, width, height);
390                    }
391
392                    /// Internally calls [`crate::wayland_adapter::SpellWin::set_exclusive_zone`]
393                    pub fn set_exclusive_zone(&mut self, val: i32) {
394                        self.way.set_exclusive_zone(val);
395                    }
396                    /// Returns a handle of [`crate::wayland_adapter::WinHandle`] to invoke wayland specific features.
397                    pub fn get_handler(&self) -> WinHandle {
398                        WinHandle(self.way.loop_handle.clone())
399                    }
400
401                    pub fn parts(self) -> ($slint_win, SpellWin) {
402                        let [<$slint_win Spell>] { ui, way } = self;
403                        (ui, way)
404                    }
405                }
406
407                impl $crate::SpellAssociatedNew for [<$slint_win Spell>] {
408                    fn on_call(
409                        &mut self,
410                    ) -> Result<(), Box<dyn std::error::Error>> {
411                        let event_loop = self.way.event_loop.clone();
412                        event_loop
413                            .borrow_mut()
414                            .dispatch(std::time::Duration::from_millis(1), &mut self.way)
415                            .unwrap();
416                        Ok(())
417                    }
418
419                    fn get_span(&self) -> tracing::span::Span {
420                        self.way.span.clone()
421                    }
422                }
423
424                impl std::ops::Deref for [<$slint_win Spell>] {
425                    type Target = [<$slint_win>];
426                    fn deref(&self) -> &Self::Target {
427                        &self.ui
428                    }
429                }
430            )?
431        }
432    };
433}
434
435#[macro_export]
436macro_rules! cast_spell {
437    // Single window (non-IPC)
438    (
439        $win:expr
440        $(, Notification: $noti:expr)?
441        $(,)?
442    ) => {{
443        $(
444            $crate::cast_spell!(@notification $noti);
445        )?
446        $crate::cast_spell!(@expand entry: $win)
447    }};
448    // Single window (IPC)
449    (
450        ($win:expr, ipc)
451        $(, Notification: $noti:expr)?
452        $(,)?
453    ) => {{
454        $(
455            $crate::cast_spell!(@notification $noti);
456        )?
457        $crate::cast_spell!(@expand entry: ($win, ipc))
458    }};
459
460    // Multiple windows (mixed IPC / non-IPC) (Defined individually)
461    (
462        windows: [ $($entry:tt),+ $(,)? ]
463        $(, Notification: $noti:expr)?
464        $(,)?
465    ) => {{
466        $(
467            $crate::cast_spell!(@notification $noti);
468        )?
469        let mut windows = Vec::new();
470        $(
471            $crate::cast_spell!(@expand entry: $entry);
472            $crate::cast_spell!(@vector_add windows, $entry);
473        )+
474        $crate::cast_spells_new(windows)
475    }};
476    //
477    // // Multiple windows (mixed IPC / non-IPC) (Defined as non-ipc vector)
478    // (
479    //     windows: $windows:expr
480    //     $(, windows_ipc: $windows_ipc:expr)?
481    //     $(, Notification: $noti:expr)?
482    //     $(,)?
483    // ) => {{
484    //     $(
485    //         $crate::cast_spell!(@notification $noti);
486    //     )?
487    //     $crate::cast_spells_new(windows)
488    // }};
489    //
490    // INTERNAL EXPANSION RULES
491    // ==================================================
492
493    // IPC-enabled window
494    (
495        @expand
496        entry: ($waywindow:expr, ipc)
497    ) => {{
498        use $crate::smithay_client_toolkit::{
499            reexports::{
500                calloop::{
501                    self,
502                    generic::Generic, PostAction,
503                    EventLoop,
504                    timer::{TimeoutAction, Timer},
505                }
506            }
507        };
508        use $crate::tracing;
509        use std::{os::unix::{net::UnixListener, io::AsRawFd}, io::prelude::*};
510        let socket_path = format!("/tmp/{}_ipc.sock", $waywindow.way.layer_name);
511        let _ = std::fs::remove_file(&socket_path); // Cleanup old socket
512        let listener = UnixListener::bind(&socket_path)?;
513        listener.set_nonblocking(true)?;
514        // let handle_weak = $waywindow.ui.as_weak().clone();
515        // $waywindow.way.ipc_listener.replace(Some(listener.try_clone().expect("Couldn't clone the listener")));
516        let (ui, way) = $waywindow.parts();
517        way.loop_handle.clone().insert_source(
518            Generic::new(listener, calloop::Interest::READ, calloop::Mode::Level),
519            move |_, meta, data| {
520                println!("{:?}", meta);
521                loop {
522                    // match data.ipc_listener.borrow().as_ref().unwrap().accept() {
523                    match meta.accept() {
524                        Ok((mut stream, _addr)) => {
525                            let mut request = String::new();
526                            // tracing::info!("new connection");
527                            if let Err(err) = stream.read_to_string(&mut request) {
528                                // tracing::warn!("Couldn't read CLI stream");
529                            }
530                            let (operation, command_args) = request.split_once(" ").unwrap();
531                            let (command, args) = command_args.split_once(" ").unwrap_or((command_args.trim(), ""));
532                            println!("Operation:{}, Command {}, args: {}",operation, command, args);
533                            match operation {
534                                "hide" => data.hide(),
535                                "show" => data.show_again(),
536                                // "update" => match format!("set_{}",command).as_str() {
537                                //     $(
538                                //         stringify!($name) => handle_weak.unwrap().$name(args.trim().parse::<$ty>().unwrap()),
539                                //     );*
540                                //     _=> {}
541                                // },
542                                "update" => {
543                                    IpcController::get_type(&ui,command);
544                                }
545                                "look"=> IpcController::change_val(&mut ui, command, args),
546                                // TODO provide mechanism for custom calls from the below
547                                // matching.
548                                _=> {}
549                            }
550                        }
551                        Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
552                            break; // drained all pending connections
553                        }
554                        Err(e) => {
555                            // tracing::warn!("Error Reading Socket: {e}");
556                            break;
557                        }
558                    }
559                }
560                Ok(PostAction::Continue)
561            },
562        );
563        $crate::cast_spell!(@run way)
564    }};
565    // Non-IPC window
566    (
567        @expand
568        entry: $waywindow:expr
569    ) => {{
570        use $crate::smithay_client_toolkit::{
571            reexports::{
572                calloop::{
573                    self,
574                    generic::Generic, PostAction,
575                    EventLoop,
576                    timer::{TimeoutAction, Timer},
577                }
578            }
579        };
580        use $crate::tracing;
581        use std::{os::unix::{net::UnixListener, io::AsRawFd}, io::prelude::*};
582        let socket_path = format!("/tmp/{}_ipc.sock", $waywindow.way.layer_name);
583        let _ = std::fs::remove_file(&socket_path); // Cleanup old socket
584        let listener = UnixListener::bind(&socket_path)?;
585        listener.set_nonblocking(true)?;
586        // let handle_weak = $waywindow.ui.as_weak().clone();
587        // $waywindow.way.ipc_listener.replace(Some(listener.try_clone().expect("Couldn't clone the listener")));
588        let (ui, way) = $waywindow.parts();
589        way.loop_handle.clone().insert_source(
590            Generic::new(listener, calloop::Interest::READ, calloop::Mode::Level),
591            move |_, meta, data| {
592                println!("{:?}", meta);
593                loop {
594                    // match data.ipc_listener.borrow().as_ref().unwrap().accept() {
595                    match meta.accept() {
596                        Ok((mut stream, _addr)) => {
597                            let mut request = String::new();
598                            // tracing::info!("new connection");
599                            if let Err(err) = stream.read_to_string(&mut request) {
600                                // tracing::warn!("Couldn't read CLI stream");
601                            }
602                            let (operation, command_args) = request.split_once(" ").unwrap();
603                            let (command, args) = command_args.split_once(" ").unwrap_or((command_args.trim(), ""));
604                            println!("Operation:{}, Command {}, args: {}",operation, command, args);
605                            match operation {
606                                "hide" => data.hide(),
607                                "show" => data.show_again(),
608                                "update" => {}
609                                "look"=> {}
610                                _=> {}
611                            }
612                        }
613                        Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
614                            break; // drained all pending connections;
615                        }
616                        Err(e) => {
617                            break;
618                        }
619                    }
620                }
621
622                // $(
623                //     // stringify!($name) => handle_weak.unwrap().$name(args.trim().parse::<$ty>().unwrap()),
624                //     println!("dcfv {}", stringify!($name));
625                // );*
626                Ok(PostAction::Continue)
627            },
628        );
629        $crate::cast_spell!(@run way)
630    }};
631    (@vector_add $wins:expr, ($waywindow:expr, ipc)) => {
632        wins.append(Box::new($waywindow) as Box<dyn $crate::SpellAssociated>)
633    };
634    (@vector_add $wins:expr, $waywindow:expr) => {
635        wins.append(Box::new($waywindow) as Box<dyn $crate::SpellAssociated>)
636    };
637    // Notification Logic
638    (@notification $noti:expr) => {
639        // runs ONCE
640        let _notification = &$noti;
641    };
642
643    (@run $way:expr) => {
644        $crate::cast_spell_inner($way)
645    }
646}
647
648// TODO set logging values in Option so that only a single value reads or writes.
649// TODO it is necessary to call join unwrap on spawned threads to ensure
650// that they are closed when main thread closes.
651// TODO linux's DNF Buffers needs to be used to improve rendering and avoid conversions
652// from CPU to GPU and vice versa.
653// TODO needs to have multi monitor support.
654// TO REMEMBER I removed dirty region from spellskiawinadapter but it can be added
655// if I want to make use of the dirty region information to strengthen my rendering.
656// TODO lock screen behaviour in a multi-monitor setup needs to be tested.
657// TODO t add tracing in following functions:
658// 1. secondary and primary services
659// TODO implement logiing for SpellLock.
660// TODO check if the dbus setup is working for more than 2 widgets when one is
661// primary and 2 are secondary.
662// Provide a method in the macro to disable tracing_subsriber completely for some project
663// which want's to do it themselves.
664// cast spell macro should be having following values.
665// 1. Disable log: should disable setting subscriber, generally for the project to use or for
666// someone to set their own.
667// 2. forge: provide a forge instance to run independently.
668// 3. exclusive_zone: true or false or with specified value.
669// 4. it should have the option to take a window_conf or directly the window configurations
670// into the macro, removing the need to define it previously.
671// 5. monitor: Specify the monitor to show the widget in.
672// Also, a procedural macro to mimic the functionalities of ForeignController.
673// Build a consistent error type to deal with CLI, dbus and window creation errors
674// (including window conf) more gracefully.