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                      self.way.get_handler().clone()
83                    }
84
85                    pub fn open_popup<T: $crate::PopupSlint + 'static>(
86                        &mut self,
87                        popup_conf: $crate::layer_properties::popup::PopupConf,
88                    ) -> Result<u32, Box<dyn std::error::Error>> {
89                        self.way.open_popup::<T>(popup_conf)
90                    }
91
92                    pub fn parts(self) -> ($slint_win, SpellWin) {
93                        let [<$slint_win Spell>] { ui, way } = self;
94                        (ui, way)
95                    }
96                }
97
98                // impl $crate::SpellAssociatedNew for [<$slint_win Spell>] {
99                    // fn on_call(
100                        // &mut self,
101                    // ) -> Result<(), Box<dyn std::error::Error>> {
102                      // self.way.on_call()
103                    // }
104                    // fn get_span(&self) -> $crate::macro_internal::Span {
105                        // self.way.get_span()
106                    // }
107                // }
108
109                impl std::ops::Deref for [<$slint_win Spell>] {
110                    type Target = [<$slint_win>];
111                    fn deref(&self) -> &Self::Target {
112                        &self.ui
113                    }
114                }
115            )+
116        }
117    };
118}
119
120#[doc = include_str!("../docs/cast_spell.md")]
121#[macro_export]
122macro_rules! cast_spell {
123    // Single window (non-IPC)
124    (
125        $win:expr
126        $(, notification: $noti:expr)?
127        $(,)?
128    ) => {{
129        let (ui, mut way) = $win.parts();
130        let mut windows = Vec::new();
131        $(
132            let (ui_noti, mut way_noti) = $noti.parts();
133            $crate::cast_spell!(@notification &way_noti, ui_noti);
134            windows.push(Box::new(way_noti) as Box<dyn $crate::SpellAssociatedNew>);
135        )?
136        $crate::cast_spell!(@expand entry: way, ui);
137        windows.push(Box::new(way) as Box<dyn $crate::SpellAssociatedNew>);
138        $crate::cast_spells_new(windows)
139        // $crate::cast_spell!(@run x)
140    }};
141    // Single window (IPC)
142    (
143        ($win:expr, ipc)
144        $(, notification: $noti:expr)?
145        $(,)?
146    ) => {{
147        let (ui, mut way) = $win.parts();
148        let mut windows = Vec::new();
149        $(
150            let (ui_noti, mut way_noti) = $noti.parts();
151            $crate::cast_spell!(@notification &way_noti, ui_noti);
152            windows.push(Box::new(way_noti) as Box<dyn $crate::SpellAssociatedNew>);
153        )?
154        $crate::cast_spell!(@expand entry: (way, ipc), ui: ui );
155        windows.push(Box::new(way) as Box<dyn $crate::SpellAssociatedNew>);
156        $crate::cast_spells_new(windows)
157        // $crate::cast_spell!(@run x)
158    }};
159
160    // Multiple windows (mixed IPC / non-IPC) (Defined individually)
161    (
162        windows: [ $($entry:tt),+ $(,)? ]
163        $(, notification: $noti:expr)?
164        $(,)?
165    ) => {{
166        let mut windows = Vec::new();
167        let mut _ui_handles: Vec<Box<dyn std::any::Any>> = Vec::new();
168        $(
169            let (ui_noti, mut way_noti) = $noti.parts();
170            $crate::cast_spell!(@notification &way_noti, ui_noti);
171            windows.push(Box::new(way_noti) as Box<dyn $crate::SpellAssociatedNew>);
172        )?
173        $(
174            let (ui, mut way) = $crate::cast_spell!(@handle_entry $entry);
175            _ui_handles.push(Box::new(ui));
176            windows.push(Box::new(way) as Box<dyn $crate::SpellAssociatedNew>);
177        )+
178        $crate::cast_spells_new(windows)
179    }};
180
181    // Moved to next release, only for non -ipc scenarios
182    // // Multiple windows (mixed IPC / non-IPC) (Defined as non-ipc vector)
183    // Older Implementation needs updation
184    // (
185    //     windows: $windows:expr
186    //     $(, windows_ipc: $windows_ipc:expr)?
187    //     $(, Notification: $noti:expr)?
188    //     $(,)?
189    // ) => {{
190    //     $(
191    //         $crate::cast_spell!(@notification $noti);
192    //     )?
193    //     $crate::cast_spells_new(windows)
194    // }};
195
196    (
197        notification: $noti:expr
198        $(,)?
199    ) => {{
200        // let (ui, mut way) = $win.parts();
201        let mut windows = Vec::new();
202        let (ui_noti, mut way_noti) = $noti.parts();
203        $crate::cast_spell!(@notification &way_noti, ui_noti);
204        windows.push(Box::new(way_noti) as Box<dyn $crate::SpellAssociatedNew>);
205        // $crate::cast_spell!(@expand entry: (way, ipc), ui: ui );
206        // windows.push(Box::new(way) as Box<dyn $crate::SpellAssociatedNew>);
207        $crate::cast_spells_new(windows)
208        // $crate::cast_spell!(@run x)
209    }};
210    // INTERNAL EXPANSION RULES
211    // ==================================================
212
213    // IPC-enabled window
214    (
215        @expand
216        entry: ($way:expr, ipc),
217        ui: $ui: expr
218    ) => {
219        let socket_path = format!("/tmp/{}_ipc.sock", $way.layer_name);
220        let _ = std::fs::remove_file(&socket_path); // Cleanup old socket
221        let listener = std::os::unix::net::UnixListener::bind(&socket_path)?;
222        let listener_clone = listener.try_clone().unwrap();
223        listener.set_nonblocking(true)?;
224        $way.ipc_handler = Some(listener_clone);
225        let _ = $way.get_handler().0.insert_source(
226            $crate::macro_internal::Generic::new(listener, $crate::macro_internal::Interest::READ, $crate::macro_internal::Mode::Level),
227            move |_, _, data| {
228                loop {
229                    match data.ipc_handler.as_ref().unwrap().accept() {
230                        Ok((mut stream, _addr)) => {
231                            let mut request = String::new();
232                            // tracing::info!("new connection");
233                            if let Err(err) = std::io::Read::read_to_string(&mut stream, &mut request) {
234                                $crate::macro_internal::warn!("Couldn't read CLI stream");
235                            }
236                            let (operation, command_args) = request.split_once(" ").unwrap_or((request.trim(), ""));
237                            let (command, args) = command_args.split_once(" ").unwrap_or((command_args.trim(), ""));
238                            match operation {
239                                "hide" => data.hide(),
240                                "show" => data.show_again(),
241                                "update" => {
242                                    IpcController::change_val(& $ui, command, args);
243                                }
244                                "look"=> {
245                                    let returned_type = IpcController::get_type(& $ui,command);
246                                    if let Err(_) = stream.write_all(returned_type.as_bytes()) {
247                                        // warn!("Couldn't send back return type");
248                                    }
249                                }
250                                // TODO provide mechanism for custom calls from the below
251                                // matching.
252                                comm => {
253                                    IpcController::custom_command(& $ui, comm);
254                                }
255                            }
256                        }
257                        Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
258                            break; // drained all pending connections
259                        }
260                        Err(e) => {
261                            // tracing::warn!("Error Reading Socket: {e}");
262                            break;
263                        }
264                    }
265                }
266                Ok($crate::macro_internal::PostAction::Continue)
267            },
268        );
269    };
270    // Non-IPC window
271    (
272        @expand
273        entry: $way:expr,
274        $_ui: expr
275    ) => {
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.get_handler().0.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}