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::{
29        configure::WindowConf,
30        dbus_window_state::{DataType, ForeignController},
31    };
32    pub use smithay_client_toolkit::reexports::calloop::timer::{TimeoutAction, Timer};
33    pub use smithay_client_toolkit::shell::wlr_layer::Anchor as LayerAnchor;
34    pub use smithay_client_toolkit::shell::wlr_layer::KeyboardInteractivity as BoardType;
35    pub use smithay_client_toolkit::shell::wlr_layer::Layer as LayerType;
36}
37
38use dbus_window_state::{ForeignController, InternalHandle, deploy_zbus_service};
39use smithay_client_toolkit::reexports::client::{DispatchError, backend};
40use std::{
41    error::Error,
42    sync::{Arc, RwLock},
43};
44use tokio::sync::mpsc;
45use wayland_adapter::SpellWin;
46
47use zbus::Error as BusError;
48
49/// It is a enum which is passed over to the sender for invoking wayland specific
50/// method calls.
51#[derive(Debug)]
52pub enum Handle {
53    /// Internally calls [`wayland_adapter::SpellWin::hide`]
54    HideWindow,
55    /// Internally calls [`wayland_adapter::SpellWin::show_again`]
56    ShowWinAgain,
57    /// Internally calls [`wayland_adapter::SpellWin::toggle`]
58    ToggleWindow,
59    /// Internally calls [`wayland_adapter::SpellWin::grab_focus`]
60    GrabKeyboardFocus,
61    /// Internally calls [`wayland_adapter::SpellWin::remove_focus`]
62    RemoveKeyboardFocus,
63    /// Internally calls [`wayland_adapter::SpellWin::add_input_region`]
64    AddInputRegion(i32, i32, i32, i32),
65    /// Internally calls [`wayland_adapter::SpellWin::subtract_input_region`]
66    SubtractInputRegion(i32, i32, i32, i32),
67    /// Internally calls [`wayland_adapter::SpellWin::add_opaque_region`]
68    AddOpaqueRegion(i32, i32, i32, i32),
69    /// Internally calls [`wayland_adapter::SpellWin::subtract_opaque_region`]
70    SubtractOpaqueRegion(i32, i32, i32, i32),
71}
72
73type State = Arc<RwLock<Box<dyn ForeignController>>>;
74
75pub fn enchant_spells<F>(
76    mut waywindows: Vec<SpellWin>,
77    // window_handles: Vec<Option<std::sync::mpsc::Receiver<Handle>>>,
78    states: Vec<Option<State>>,
79    mut set_callbacks: Vec<Option<F>>,
80) -> Result<(), Box<dyn Error>>
81where
82    F: FnMut(Arc<RwLock<Box<dyn ForeignController>>>),
83{
84    if waywindows.len() == states.len() && waywindows.len() == set_callbacks.len() {
85        let mut internal_recievers: Vec<mpsc::Receiver<InternalHandle>> = Vec::new();
86        states.iter().enumerate().for_each(|(index, state)| {
87            internal_recievers.push(helper_fn_for_deploy(
88                waywindows[index].layer_name.clone(),
89                state,
90            ));
91        });
92
93        loop {
94            states.iter().enumerate().for_each(|(index, state)| {
95                helper_fn_internal_handle(
96                    state,
97                    &mut set_callbacks[index],
98                    &mut internal_recievers[index],
99                    &mut waywindows[index],
100                );
101            });
102            waywindows.iter_mut().for_each(|win| {
103                helper_fn_win_handle(win);
104            });
105
106            let _: Vec<_> = waywindows
107                .iter_mut()
108                .map(|waywindow| {
109                    run_loop_once(waywindow);
110                })
111                .collect();
112        }
113    } else {
114        panic!(
115            "The lengths of given vectors are not equal. \n Make sure that given vector lengths are equal"
116        );
117    }
118}
119
120pub fn cast_spell<F>(
121    mut waywindow: SpellWin,
122    // mut event_queue: EventQueue<SpellWinInternal>,
123    // window_handle_option: Option<std::sync::mpsc::Receiver<Handle>>,
124    state: Option<Arc<RwLock<Box<dyn ForeignController>>>>,
125    mut set_callback: Option<F>,
126) -> Result<(), Box<dyn Error>>
127where
128    F: FnMut(Arc<RwLock<Box<dyn ForeignController>>>),
129{
130    let mut rx = helper_fn_for_deploy(waywindow.layer_name.clone(), &state);
131    loop {
132        helper_fn_internal_handle(&state, &mut set_callback, &mut rx, &mut waywindow);
133        helper_fn_win_handle(&mut waywindow);
134        run_loop_once(&mut waywindow);
135    }
136}
137
138fn helper_fn_internal_handle<F>(
139    state: &Option<Arc<RwLock<Box<dyn ForeignController>>>>,
140    set_callback: &mut Option<F>,
141    rx: &mut mpsc::Receiver<InternalHandle>,
142    waywindow: &mut SpellWin,
143) where
144    F: FnMut(Arc<RwLock<Box<dyn ForeignController>>>),
145{
146    if let Some(callback) = set_callback
147        && state.is_some()
148        && let Ok(int_handle) = rx.try_recv()
149    {
150        match int_handle {
151            InternalHandle::StateValChange((key, data_type)) => {
152                println!("Inside statechange");
153                //Glad I could think of this sub scope for RwLock.
154                {
155                    let mut state_inst = state.as_ref().unwrap().write().unwrap();
156                    state_inst.change_val(&key, data_type);
157                }
158                callback(state.as_ref().unwrap().clone());
159            }
160            InternalHandle::ShowWinAgain => {
161                waywindow.show_again();
162            }
163            InternalHandle::HideWindow => waywindow.hide(),
164        }
165    };
166}
167
168fn helper_fn_for_deploy(
169    layer_name: String,
170    state: &Option<Arc<RwLock<Box<dyn ForeignController>>>>,
171) -> mpsc::Receiver<InternalHandle> {
172    let (tx, rx) = mpsc::channel::<InternalHandle>(20);
173    if let Some(some_state) = state {
174        let state_clone = some_state.clone();
175        std::thread::spawn(|| {
176            let rt = tokio::runtime::Builder::new_current_thread()
177                .enable_all()
178                .build()
179                .unwrap();
180            if let Err(error) = rt.block_on(async move {
181                println!("deploied zbus serive in thread");
182                deploy_zbus_service(state_clone, tx, layer_name).await?;
183                Ok::<_, BusError>(())
184            }) {
185                println!("Dbus Thread panicked witth the following error. \n {error}");
186                panic!("All code panicked");
187            }
188        });
189    }
190    rx
191}
192
193fn helper_fn_win_handle(waywindow: &mut SpellWin) {
194    if let Some(window_handle) = &waywindow.handler
195        && let Ok(handle) = window_handle.try_recv()
196    {
197        match handle {
198            Handle::HideWindow => waywindow.hide(),
199            Handle::ShowWinAgain => waywindow.show_again(),
200            Handle::ToggleWindow => waywindow.toggle(),
201            Handle::GrabKeyboardFocus => waywindow.grab_focus(),
202            Handle::RemoveKeyboardFocus => waywindow.remove_focus(),
203            Handle::AddInputRegion(x, y, width, height) => {
204                waywindow.add_input_region(x, y, width, height);
205            }
206            Handle::SubtractInputRegion(x, y, width, height) => {
207                waywindow.subtract_input_region(x, y, width, height);
208            }
209            Handle::AddOpaqueRegion(x, y, width, height) => {
210                waywindow.add_opaque_region(x, y, width, height);
211            }
212            Handle::SubtractOpaqueRegion(x, y, width, height) => {
213                waywindow.subtract_opaque_region(x, y, width, height);
214            }
215        }
216    }
217}
218
219fn run_loop_once(waywindow: &mut SpellWin) {
220    let is_first_config = waywindow.first_configure;
221    let queue = waywindow.queue.clone();
222    if is_first_config {
223        // Primary erros are handled here in the first configration itself.
224        if let Err(err_value) = queue.borrow_mut().roundtrip(waywindow) {
225            report_error(err_value);
226        }
227        // event_queue.roundtrip(&mut waywindow).unwrap();
228    } else {
229        if let Err(err_val) = queue.borrow_mut().blocking_dispatch(waywindow) {
230            panic!("{}", err_val);
231        }
232        // waywindow.queue.borrow().flush().unwrap();
233        // queue.borrow_mut().dispatch_pending(waywindow).unwrap();
234        // if let Some(read_value) = waywindow.queue.borrow().prepare_read() {
235        //     let _ = read_value.read();
236        // }
237    }
238}
239
240fn report_error(error_value: DispatchError) {
241    match error_value {
242        DispatchError::Backend(backend) => match backend {
243            backend::WaylandError::Io(std_error) => panic!("{}", std_error),
244            backend::WaylandError::Protocol(protocol) => {
245                if protocol.code == 2 && protocol.object_id == 6 {
246                    panic!("Maybe the width or height zero");
247                }
248            }
249        },
250        DispatchError::BadMessage {
251            sender_id,
252            interface,
253            opcode,
254        } => panic!("BadMessage Error: sender: {sender_id} interface: {interface} opcode {opcode}"),
255    }
256}
257// TODO it is necessary to call join unwrap on spawned threads to ensure
258// that they are closed when main thread closes.
259// TODO see if the above loop can be replaced by callloop for better idomicity and
260// performance in any sense.
261// TODO The project should have a live preview feature. It can be made by leveraging
262// slint's preview and moving the output of debug to spell_cli.
263// TODO linux's DNF Buffers needs to be used to improve rendering and avoid conversions
264// from CPU to GPU and vice versa.
265// Replace the expect statements in the code with tracing statements.
266// TODO needs to have multi monitor support.
267// TO REMEMBER I removed dirty region from spellskiawinadapter but it can be added
268// if I want to make use of the dirty region information to strengthen my rendering.
269// A Bug effects multi widget setup where is invoke_callback is called, first draw
270// keeps on drawing on the closed window, can only be debugged after window wise logs
271// are enabled. example is saved in a bin file called bug_multi.rs
272// TODO to check what will happen to my dbus network if windows with same layer name will be
273// present. To check causes for errors as well as before implenenting muliple layers in same
274// window.
275// TODO lock screen behaviour in a multi-monitor setup needs to be tested.
276// TODO merge cast_Spell with run_lock after implementing calloop in normal windows.