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