Skip to main content

spell_framework/
event_macros.rs

1#[doc = include_str!("../docs/generate_widgets.md")]
2#[macro_export]
3macro_rules! generate_widgets {
4    ($($slint_win:ty),+) => {
5        use $crate::wayland_adapter::{WinHandle, SpellWin};
6        #[allow(unused_imports)]
7        use std::io::Write;
8        $crate::macro_internal::paste! {
9            $(
10                struct [<$slint_win Spell>] {
11                    ui: $slint_win ,
12                    way: SpellWin,
13                }
14
15                impl std::fmt::Debug for [<$slint_win Spell>] {
16                    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17                        f.debug_struct("Spell")
18                        .field("wayland_side:", &self.way) // Add fields by name
19                        .finish() // Finalize the struct formatting
20                    }
21                }
22
23                impl [<$slint_win Spell>] {
24                    pub fn invoke_spell(name: &str, window_conf: WindowConf) -> Self {
25                        let way_win = SpellWin::invoke_spell(name, window_conf);
26                        [<$slint_win Spell>] {
27                            ui: $slint_win::new().unwrap(),
28                            way: way_win
29                        }
30                    }
31                    /// Internally calls [`crate::wayland_adapter::SpellWin::hide`]
32                    pub fn hide(&self) {
33                        self.way.hide();
34                    }
35
36                    /// Internally calls [`crate::wayland_adapter::SpellWin::show_again`]
37                    pub fn show_again(&mut self) {
38                        self.way.show_again();
39                    }
40
41                    /// Internally calls [`crate::wayland_adapter::SpellWin::toggle`]
42                    pub fn toggle(&mut self) {
43                        self.way.toggle();
44                    }
45
46                    /// Internally calls [`crate::wayland_adapter::SpellWin::grab_focus`]
47                    pub fn grab_focus(&self) {
48                        self.way.grab_focus();
49                    }
50
51                    /// Internally calls [`crate::wayland_adapter::SpellWin::remove_focus`]
52                    pub fn remove_focus(&self) {
53                        self.way.remove_focus();
54                    }
55
56                    /// Internally calls [`crate::wayland_adapter::SpellWin::add_input_region`]
57                    pub fn add_input_region(&self, x: i32, y: i32, width: i32, height: i32) {
58                        self.way.add_input_region(x, y, width, height);
59                    }
60
61                    /// Internally calls [`crate::wayland_adapter::SpellWin::subtract_input_region`]
62                    pub fn subtract_input_region(&self, x: i32, y: i32, width: i32, height: i32) {
63                        self.way.subtract_input_region(x, y, width, height);
64                    }
65
66                    /// Internally calls [`crate::wayland_adapter::SpellWin::add_opaque_region`]
67                    pub fn add_opaque_region(&self, x: i32, y: i32, width: i32, height: i32) {
68                        self.way.add_opaque_region(x, y, width, height);
69                    }
70
71                    /// Internally calls [`crate::wayland_adapter::SpellWin::subtract_opaque_region`]
72                    pub fn subtract_opaque_region(&self, x: i32, y: i32, width: i32, height: i32) {
73                        self.way.subtract_opaque_region(x, y, width, height);
74                    }
75
76                    /// Internally calls [`crate::wayland_adapter::SpellWin::set_exclusive_zone`]
77                    pub fn set_exclusive_zone(&mut self, val: i32) {
78                        self.way.set_exclusive_zone(val);
79                    }
80                    /// Returns a handle of [`crate::wayland_adapter::WinHandle`] to invoke wayland specific features.
81                    pub fn get_handler(&self) -> WinHandle {
82                        WinHandle(self.way.loop_handle.clone())
83                    }
84
85                    pub fn parts(self) -> ($slint_win, SpellWin) {
86                        let [<$slint_win Spell>] { ui, way } = self;
87                        (ui, way)
88                    }
89                }
90
91                impl $crate::SpellAssociatedNew for [<$slint_win Spell>] {
92                    fn on_call(
93                        &mut self,
94                    ) -> Result<(), Box<dyn std::error::Error>> {
95                        let event_loop = self.way.event_loop.clone();
96                        event_loop
97                            .borrow_mut()
98                            .dispatch(std::time::Duration::from_millis(1), &mut self.way)
99                            .unwrap();
100                        Ok(())
101                    }
102
103                    fn get_span(&self) -> $crate::macro_internal::Span {
104                        self.way.span.clone()
105                    }
106                }
107
108                impl std::ops::Deref for [<$slint_win Spell>] {
109                    type Target = [<$slint_win>];
110                    fn deref(&self) -> &Self::Target {
111                        &self.ui
112                    }
113                }
114            )+
115        }
116    };
117}
118
119#[doc = include_str!("../docs/cast_spell.md")]
120#[macro_export]
121macro_rules! cast_spell {
122    // Single window (non-IPC)
123    (
124        $win:expr
125        $(, notification: $noti:expr)?
126        $(,)?
127    ) => {{
128        let (ui, mut way) = $win.parts();
129        let mut windows = Vec::new();
130        $(
131            let (ui_noti, mut way_noti) = $noti.parts();
132            $crate::cast_spell!(@notification &way_noti, ui_noti);
133            windows.push(Box::new(way_noti) as Box<dyn $crate::SpellAssociatedNew>);
134        )?
135        $crate::cast_spell!(@expand entry: way, ui);
136        windows.push(Box::new(way) as Box<dyn $crate::SpellAssociatedNew>);
137        $crate::cast_spells_new(windows)
138        // $crate::cast_spell!(@run x)
139    }};
140    // Single window (IPC)
141    (
142        ($win:expr, ipc)
143        $(, notification: $noti:expr)?
144        $(,)?
145    ) => {{
146        let (ui, mut way) = $win.parts();
147        let mut windows = Vec::new();
148        $(
149            let (ui_noti, mut way_noti) = $noti.parts();
150            $crate::cast_spell!(@notification &way_noti, ui_noti);
151            windows.push(Box::new(way_noti) as Box<dyn $crate::SpellAssociatedNew>);
152        )?
153        $crate::cast_spell!(@expand entry: (way, ipc), ui: ui );
154        windows.push(Box::new(way) as Box<dyn $crate::SpellAssociatedNew>);
155        $crate::cast_spells_new(windows)
156        // $crate::cast_spell!(@run x)
157    }};
158
159    // Multiple windows (mixed IPC / non-IPC) (Defined individually)
160    (
161        windows: [ $($entry:tt),+ $(,)? ]
162        $(, notification: $noti:expr)?
163        $(,)?
164    ) => {{
165        let mut windows = Vec::new();
166        let mut _ui_handles: Vec<Box<dyn std::any::Any>> = Vec::new();
167        $(
168            let (ui_noti, mut way_noti) = $noti.parts();
169            $crate::cast_spell!(@notification &way_noti, ui_noti);
170            windows.push(Box::new(way_noti) as Box<dyn $crate::SpellAssociatedNew>);
171        )?
172        $(
173            let (ui, mut way) = $crate::cast_spell!(@handle_entry $entry);
174            _ui_handles.push(Box::new(ui));
175            windows.push(Box::new(way) as Box<dyn $crate::SpellAssociatedNew>);
176        )+
177        $crate::cast_spells_new(windows)
178    }};
179
180    // Moved to next release, only for non -ipc scenarios
181    // // Multiple windows (mixed IPC / non-IPC) (Defined as non-ipc vector)
182    // Older Implementation needs updation
183    // (
184    //     windows: $windows:expr
185    //     $(, windows_ipc: $windows_ipc:expr)?
186    //     $(, Notification: $noti:expr)?
187    //     $(,)?
188    // ) => {{
189    //     $(
190    //         $crate::cast_spell!(@notification $noti);
191    //     )?
192    //     $crate::cast_spells_new(windows)
193    // }};
194
195    (
196        notification: $noti:expr
197        $(,)?
198    ) => {{
199        // let (ui, mut way) = $win.parts();
200        let mut windows = Vec::new();
201        let (ui_noti, mut way_noti) = $noti.parts();
202        $crate::cast_spell!(@notification &way_noti, ui_noti);
203        windows.push(Box::new(way_noti) as Box<dyn $crate::SpellAssociatedNew>);
204        // $crate::cast_spell!(@expand entry: (way, ipc), ui: ui );
205        // windows.push(Box::new(way) as Box<dyn $crate::SpellAssociatedNew>);
206        $crate::cast_spells_new(windows)
207        // $crate::cast_spell!(@run x)
208    }};
209    // INTERNAL EXPANSION RULES
210    // ==================================================
211
212    // IPC-enabled window
213    (
214        @expand
215        entry: ($way:expr, ipc),
216        ui: $ui: expr
217    ) => {
218        let socket_path = format!("/tmp/{}_ipc.sock", $way.layer_name);
219        let _ = std::fs::remove_file(&socket_path); // Cleanup old socket
220        let listener = std::os::unix::net::UnixListener::bind(&socket_path)?;
221        let listener_clone = listener.try_clone().unwrap();
222        listener.set_nonblocking(true)?;
223        $way.ipc_handler = Some(listener_clone);
224        let _ = $way.loop_handle.clone().insert_source(
225            $crate::macro_internal::Generic::new(listener, $crate::macro_internal::Interest::READ, $crate::macro_internal::Mode::Level),
226            move |_, _, data| {
227                loop {
228                    match data.ipc_handler.as_ref().unwrap().accept() {
229                        Ok((mut stream, _addr)) => {
230                            let mut request = String::new();
231                            // tracing::info!("new connection");
232                            if let Err(err) = std::io::Read::read_to_string(&mut stream, &mut request) {
233                                $crate::macro_internal::warn!("Couldn't read CLI stream");
234                            }
235                            let (operation, command_args) = request.split_once(" ").unwrap_or((request.trim(), ""));
236                            let (command, args) = command_args.split_once(" ").unwrap_or((command_args.trim(), ""));
237                            match operation {
238                                "hide" => data.hide(),
239                                "show" => data.show_again(),
240                                "update" => {
241                                    IpcController::change_val(& $ui, command, args);
242                                }
243                                "look"=> {
244                                    let returned_type = IpcController::get_type(& $ui,command);
245                                    if let Err(_) = stream.write_all(returned_type.as_bytes()) {
246                                        // warn!("Couldn't send back return type");
247                                    }
248                                }
249                                // TODO provide mechanism for custom calls from the below
250                                // matching.
251                                comm => {
252                                    IpcController::custom_command(& $ui, comm);
253                                }
254                            }
255                        }
256                        Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
257                            break; // drained all pending connections
258                        }
259                        Err(e) => {
260                            // tracing::warn!("Error Reading Socket: {e}");
261                            break;
262                        }
263                    }
264                }
265                Ok($crate::macro_internal::PostAction::Continue)
266            },
267        );
268    };
269    // Non-IPC window
270    (
271        @expand
272        entry: $way:expr,
273        $_ui: expr
274    ) => {
275        // use std::{os::unix::{net::UnixListener, io::AsRawFd}, io::prelude::*};
276        let socket_path = format!("/tmp/{}_ipc.sock", $way.layer_name);
277        let _ = std::fs::remove_file(&socket_path); // Cleanup old socket
278        let listener = std::os::unix::net::UnixListener::bind(&socket_path)?;
279        let listener_clone = listener.try_clone().unwrap();
280        listener.set_nonblocking(true)?;
281        $way.ipc_handler = Some(listener_clone);
282        let _ = $way.loop_handle.clone().insert_source(
283            $crate::macro_internal::Generic::new(listener, $crate::macro_internal::Interest::READ, $crate::macro_internal::Mode::Level),
284            move |_, _, data| {
285                loop {
286                    match data.ipc_handler.as_ref().unwrap().accept() {
287                        Ok((mut stream, _addr)) => {
288                            let mut request = String::new();
289                            // tracing::info!("new connection");
290                            if let Err(err) = std::io::Read::read_to_string(&mut stream, &mut request) {
291                                $crate::macro_internal::warn!("Couldn't read CLI stream");
292                            }
293                            let (operation, command_args) = request.split_once(" ").unwrap_or((request.trim(), ""));
294                            // These info statements doesn't seem to be working due to them running in the wrong space.
295                            $crate::macro_internal::info!("Operation:{}, command_args:{}", operation, command_args);
296                            let (command, args) = command_args.split_once(" ").unwrap_or((command_args.trim(), ""));
297                            $crate::macro_internal::info!("Operation:{}, Command {}, args: {}",operation, command, args);
298                            match operation {
299                                "hide" => data.hide(),
300                                "show" => data.show_again(),
301                                "update" => {}
302                                "look"=> {}
303                                _=> {}
304                            }
305                        }
306                        Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
307                            break; // drained all pending connections;
308                        }
309                        Err(e) => {
310                            panic!("Following error occured.{}",e);
311                        }
312                    }
313                }
314                Ok($crate::macro_internal::PostAction::Continue)
315            },
316        );
317    };
318
319    // Compile-time IPC dispatch for the multi-window arm.
320    // Both arms return (ui, way) so the caller can keep ui alive in the outer scope.
321    // For IPC: ui is moved into the event-loop closure, so clone it first and
322    // return the original handle to the caller.
323    (@handle_entry ($combowin:expr, ipc)) => {{
324        let (ui, mut way) = $combowin.parts();
325        // let _ui_for_closure = ui.clone();
326        $crate::cast_spell!(@expand entry: (way, ipc), ui: ui);
327        (String::from(""), way)
328    }};
329    (@handle_entry $combowin:expr) => {{
330        let (ui, mut way) = $combowin.parts();
331        $crate::cast_spell!(@expand entry: way, ui);
332        (ui, way)
333    }};
334
335    (@parts win: ($combowin:expr , ipc)) => {{
336        ($combowin.parts(), true)
337    }};
338    (@parts win: $combowin:expr) => {{
339        ($combowin.parts(), false)
340    }};
341    // Notification Logic
342    (@notification $noti:expr, $ui_noti: expr) => {
343        // runs ONCE
344        $crate::vault::set_notification($noti, Box::new($ui_noti)as Box<dyn $crate::vault::NotificationManager>)
345        // let _notification = &$noti;
346    };
347
348    (@run $way:expr) => {
349        $crate::cast_spell_inner($way)
350    };
351
352    // SpellLock Locking
353    (lock: $lock:expr) => {
354        $crate::cast_spell!(@run $lock)
355    };
356}