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.