1use crate::animation::{AnimationFrameData, AnimationFrameResult};
2use crate::config::Configuration;
3use crate::host::*;
4use crate::input::{KeyboardEvent, PointerEvent};
5use crate::output::*;
6use crate::placement::Placement;
7use crate::window::Window;
8use crate::workspace::*;
9
10unsafe extern "C" {
11 fn miracle_get_plugin_handle() -> u32;
12}
13
14pub fn get_userdata_json() -> Option<String> {
19 let handle = unsafe { miracle_get_plugin_handle() };
20 let mut buf = vec![0u8; 4096];
21 loop {
22 let result = unsafe {
23 crate::host::miracle_get_plugin_userdata(
24 handle,
25 buf.as_mut_ptr() as i32,
26 buf.len() as i32,
27 )
28 };
29 if result == 0 {
30 return None;
31 } else if result == -1 {
32 buf.resize(buf.len() * 2, 0);
33 } else {
34 return std::str::from_utf8(&buf[..result as usize])
35 .ok()
36 .map(|s| s.to_owned());
37 }
38 }
39}
40
41pub trait Plugin {
55 fn window_open_animation(
59 &mut self,
60 _data: &AnimationFrameData,
61 _window: &Window,
62 ) -> Option<AnimationFrameResult> {
63 None
64 }
65
66 fn window_close_animation(
70 &mut self,
71 _data: &AnimationFrameData,
72 _window: &Window,
73 ) -> Option<AnimationFrameResult> {
74 None
75 }
76
77 fn window_move_animation(
81 &mut self,
82 _data: &AnimationFrameData,
83 _window: &Window,
84 ) -> Option<AnimationFrameResult> {
85 None
86 }
87
88 fn workspace_switch_animation(
92 &mut self,
93 _data: &AnimationFrameData,
94 _workspace: &Workspace,
95 ) -> Option<AnimationFrameResult> {
96 None
97 }
98
99 fn place_new_window(&mut self, _info: &Window) -> Option<Placement> {
104 None
105 }
106
107 fn window_deleted(&mut self, _info: &Window) {}
112
113 fn window_focused(&mut self, _info: &Window) {}
115
116 fn window_unfocused(&mut self, _info: &Window) {}
118
119 fn workspace_created(&mut self, _workspace: &Workspace) {}
121
122 fn workspace_removed(&mut self, _workspace: &Workspace) {}
124
125 fn workspace_focused(&mut self, _previous_id: Option<u64>, _current: &Workspace) {}
129
130 fn workspace_area_changed(&mut self, _workspace: &Workspace) {}
132
133 fn window_workspace_changed(&mut self, _info: &Window, _workspace: &Workspace) {}
138
139 fn configure(&mut self) -> Option<Configuration> {
149 None
150 }
151
152 fn handle_keyboard_input(&mut self, _event: KeyboardEvent) -> bool {
158 false
159 }
160
161 fn handle_pointer_event(&mut self, _event: PointerEvent) -> bool {
167 false
168 }
169}
170
171pub fn managed_windows() -> Vec<Window> {
180 let handle = unsafe { miracle_get_plugin_handle() };
181 let count = unsafe { miracle_num_managed_windows(handle) };
182
183 (0..count)
184 .filter_map(|i| {
185 const NAME_BUF_LEN: usize = 256;
186 let mut window_info =
187 std::mem::MaybeUninit::<crate::bindings::miracle_window_info_t>::uninit();
188 let mut name_buf: [u8; NAME_BUF_LEN] = [0; NAME_BUF_LEN];
189
190 unsafe {
191 let result = miracle_get_managed_window_at(
192 handle,
193 i,
194 window_info.as_mut_ptr() as i32,
195 name_buf.as_mut_ptr() as i32,
196 NAME_BUF_LEN as i32,
197 );
198
199 if result != 0 {
200 return None;
201 }
202
203 let window_info = window_info.assume_init();
204 let name_len = name_buf
205 .iter()
206 .position(|&c| c == 0)
207 .unwrap_or(NAME_BUF_LEN);
208 let name = String::from_utf8_lossy(&name_buf[..name_len]).into_owned();
209
210 Some(Window::from_c_with_name(&window_info, name))
211 }
212 })
213 .collect()
214}
215
216pub fn num_outputs() -> u32 {
218 unsafe { miracle_num_outputs() }
219}
220
221pub fn get_output_at(index: u32) -> Option<Output> {
225 if index >= num_outputs() {
226 return None;
227 }
228
229 const NAME_BUF_LEN: usize = 256;
230 let mut output = std::mem::MaybeUninit::<crate::bindings::miracle_output_t>::uninit();
231 let mut name_buf: [u8; NAME_BUF_LEN] = [0; NAME_BUF_LEN];
232
233 unsafe {
234 let result = miracle_get_output_at(
235 index,
236 output.as_mut_ptr() as i32,
237 name_buf.as_mut_ptr() as i32,
238 NAME_BUF_LEN as i32,
239 );
240
241 if result != 0 {
242 return None;
243 }
244
245 let output = output.assume_init();
246
247 let name_len = name_buf
249 .iter()
250 .position(|&c| c == 0)
251 .unwrap_or(NAME_BUF_LEN);
252 let name = String::from_utf8_lossy(&name_buf[..name_len]).into_owned();
253
254 Some(Output::from_c_with_name(&output, name))
255 }
256}
257
258pub fn get_outputs() -> Vec<Output> {
260 let count = num_outputs();
261 (0..count).filter_map(get_output_at).collect()
262}
263
264pub fn get_active_workspace() -> Option<Workspace> {
268 const NAME_BUF_LEN: usize = 256;
269 let mut workspace = std::mem::MaybeUninit::<crate::bindings::miracle_workspace_t>::uninit();
270 let mut name_buf: [u8; NAME_BUF_LEN] = [0; NAME_BUF_LEN];
271
272 unsafe {
273 let result = miracle_get_active_workspace(
274 workspace.as_mut_ptr() as i32,
275 name_buf.as_mut_ptr() as i32,
276 NAME_BUF_LEN as i32,
277 );
278
279 if result != 0 {
280 return None;
281 }
282
283 let workspace = workspace.assume_init();
284 if workspace.is_set == 0 {
285 return None;
286 }
287
288 let name_len = name_buf
289 .iter()
290 .position(|&c| c == 0)
291 .unwrap_or(NAME_BUF_LEN);
292 let name = String::from_utf8_lossy(&name_buf[..name_len]).into_owned();
293
294 Some(Workspace::from_c_with_name(&workspace, name))
295 }
296}
297
298pub fn request_workspace(
307 number: Option<u32>,
308 name: Option<&str>,
309 focus: bool,
310) -> Option<Workspace> {
311 const NAME_BUF_LEN: usize = 256;
312 let mut workspace = std::mem::MaybeUninit::<crate::bindings::miracle_workspace_t>::uninit();
313 let mut out_name_buf: [u8; NAME_BUF_LEN] = [0; NAME_BUF_LEN];
314
315 let has_number: i32 = if number.is_some() { 1 } else { 0 };
316 let number_val: i32 = number.unwrap_or(0) as i32;
317
318 let name_ptr: i32 = match name {
319 Some(s) => s.as_ptr() as i32,
320 None => 0,
321 };
322 let name_len: i32 = match name {
323 Some(s) => s.len() as i32,
324 None => 0,
325 };
326
327 unsafe {
328 let result = miracle_request_workspace(
329 has_number,
330 number_val,
331 name_ptr,
332 name_len,
333 workspace.as_mut_ptr() as i32,
334 out_name_buf.as_mut_ptr() as i32,
335 NAME_BUF_LEN as i32,
336 if focus { 1 } else { 0 },
337 );
338
339 if result != 0 {
340 return None;
341 }
342
343 let workspace = workspace.assume_init();
344 if workspace.is_set == 0 {
345 return None;
346 }
347
348 let out_name_len = out_name_buf
349 .iter()
350 .position(|&c| c == 0)
351 .unwrap_or(NAME_BUF_LEN);
352 let ws_name = String::from_utf8_lossy(&out_name_buf[..out_name_len]).into_owned();
353
354 Some(Workspace::from_c_with_name(&workspace, ws_name))
355 }
356}
357
358pub fn queue_custom_animation<F>(callback: F, duration_seconds: f32) -> Option<u32>
367where
368 F: FnMut(u32, f32, f32) + 'static,
369{
370 let handle = unsafe { miracle_get_plugin_handle() };
371 let mut animation_id: u32 = 0;
372 let mut dur = duration_seconds;
373 let result = unsafe {
374 crate::host::miracle_queue_custom_animation(
375 handle as i32,
376 &mut animation_id as *mut u32 as i32,
377 &mut dur as *mut f32 as i32,
378 )
379 };
380 if result == 0 {
381 custom_anim_callbacks().insert(animation_id, (Box::new(callback), duration_seconds));
382 Some(animation_id)
383 } else {
384 None
385 }
386}
387
388pub fn register_window_shader(passes: &[&str]) -> Option<u8> {
404 let descriptors: Vec<[i32; 2]> = passes
407 .iter()
408 .map(|p| [p.as_ptr() as i32, p.len() as i32])
409 .collect();
410 let handle = unsafe { miracle_get_plugin_handle() };
411 let result = unsafe {
412 crate::host::miracle_register_window_sample_to_rgba(
413 handle as i32,
414 descriptors.as_ptr() as i32,
415 passes.len() as i32,
416 )
417 };
418 if result >= 0 {
419 Some(result as u8)
420 } else {
421 None
422 }
423}
424
425pub fn set_screen_shader(passes: &[&str]) -> Result<(), ()> {
439 let handle = unsafe { miracle_get_plugin_handle() };
440 let descriptors: Vec<[i32; 2]> = passes
442 .iter()
443 .map(|p| [p.as_ptr() as i32, p.len() as i32])
444 .collect();
445 let r = unsafe {
446 crate::host::miracle_set_screen_shader(
447 handle as i32,
448 descriptors.as_ptr() as i32,
449 passes.len() as i32,
450 )
451 };
452 if r == 0 {
453 Ok(())
454 } else {
455 Err(())
456 }
457}
458
459static mut _CUSTOM_ANIM_CALLBACKS: Option<
460 std::collections::HashMap<u32, (Box<dyn FnMut(u32, f32, f32)>, f32)>,
461> = None;
462
463#[doc(hidden)]
468pub fn custom_anim_callbacks()
469-> &'static mut std::collections::HashMap<u32, (Box<dyn FnMut(u32, f32, f32)>, f32)> {
470 unsafe {
471 if (*std::ptr::addr_of!(_CUSTOM_ANIM_CALLBACKS)).is_none() {
472 _CUSTOM_ANIM_CALLBACKS = Some(std::collections::HashMap::new());
473 }
474 (*std::ptr::addr_of_mut!(_CUSTOM_ANIM_CALLBACKS))
475 .as_mut()
476 .unwrap()
477 }
478}
479
480#[macro_export]
495macro_rules! miracle_plugin {
496 ($plugin_type:ty) => {
497 static mut _MIRACLE_PLUGIN: Option<$plugin_type> = None;
498 static mut _MIRACLE_PLUGIN_HANDLE: u32 = 0;
499
500 #[unsafe(no_mangle)]
501 pub extern "C" fn miracle_get_plugin_handle() -> u32 {
502 unsafe { _MIRACLE_PLUGIN_HANDLE }
503 }
504
505 #[unsafe(no_mangle)]
506 pub extern "C" fn init(handle: i32) {
507 unsafe {
508 _MIRACLE_PLUGIN_HANDLE = handle as u32;
509 _MIRACLE_PLUGIN = Some(<$plugin_type>::default());
510 }
511 }
512
513 #[unsafe(no_mangle)]
514 pub extern "C" fn animate(data_ptr: i32, result_ptr: i32) -> i32 {
515 let plugin = unsafe {
516 match _MIRACLE_PLUGIN.as_mut() {
517 Some(p) => p,
518 None => return 0,
519 }
520 };
521
522 let c_data = unsafe {
523 &*(data_ptr as *const $crate::bindings::miracle_plugin_animation_frame_data_t)
524 };
525 let data: $crate::animation::AnimationFrameData = (*c_data).into();
526
527 let extract_window = || {
528 let bytes = unsafe {
529 core::slice::from_raw_parts(c_data.window_name.as_ptr() as *const u8, 256)
530 };
531 let len = bytes.iter().position(|&b| b == 0).unwrap_or(256);
532 let name = String::from_utf8_lossy(&bytes[..len]).into_owned();
533 unsafe { $crate::window::Window::from_c_with_name(&c_data.window_info, name) }
534 };
535
536 let extract_workspace = || {
537 let bytes = unsafe {
538 core::slice::from_raw_parts(c_data.workspace_name.as_ptr() as *const u8, 256)
539 };
540 let len = bytes.iter().position(|&b| b == 0).unwrap_or(256);
541 let name = String::from_utf8_lossy(&bytes[..len]).into_owned();
542 unsafe { $crate::workspace::Workspace::from_c_with_name(&c_data.workspace, name) }
543 };
544
545 let write_result = |result: $crate::animation::AnimationFrameResult| {
546 let c_result: $crate::bindings::miracle_plugin_animation_frame_result_t =
547 result.into();
548 unsafe {
549 let out = &mut *(result_ptr
550 as *mut $crate::bindings::miracle_plugin_animation_frame_result_t);
551 *out = c_result;
552 }
553 };
554
555 match c_data.type_ {
556 $crate::bindings::miracle_animation_type_miracle_animation_type_window_open => {
557 let window = extract_window();
558 match plugin.window_open_animation(&data, &window) {
559 Some(result) => { write_result(result); 1 }
560 None => 0,
561 }
562 },
563 $crate::bindings::miracle_animation_type_miracle_animation_type_window_close => {
564 let window = extract_window();
565 match plugin.window_close_animation(&data, &window) {
566 Some(result) => { write_result(result); 1 }
567 None => 0,
568 }
569 },
570 $crate::bindings::miracle_animation_type_miracle_animation_type_window_move => {
571 let window = extract_window();
572 match plugin.window_move_animation(&data, &window) {
573 Some(result) => { write_result(result); 1 }
574 None => 0,
575 }
576 },
577 $crate::bindings::miracle_animation_type_miracle_animation_type_workspace_switch => {
578 let workspace = extract_workspace();
579 match plugin.workspace_switch_animation(&data, &workspace) {
580 Some(result) => { write_result(result); 1 }
581 None => 0,
582 }
583 },
584 _ => 0
585 }
586
587 }
588
589 #[unsafe(no_mangle)]
590 pub extern "C" fn custom_animate(data_ptr: i32) -> i32 {
591 let raw = unsafe {
592 &*(data_ptr as *const $crate::animation::RawCustomAnimationData)
593 };
594
595 let callbacks = $crate::plugin::custom_anim_callbacks();
596 let done = if let Some((cb, dur)) = callbacks.get_mut(&raw.animation_id) {
597 cb(raw.animation_id, raw.dt, raw.elapsed_seconds);
598 raw.elapsed_seconds >= *dur
599 } else {
600 false
601 };
602 if done {
603 callbacks.remove(&raw.animation_id);
604 }
605
606 0
608 }
609
610 #[unsafe(no_mangle)]
611 pub extern "C" fn place_new_window(
612 window_info_ptr: i32,
613 result_ptr: i32,
614 name_ptr: i32,
615 name_len: i32,
616 ) -> i32 {
617 let plugin = unsafe {
618 match _MIRACLE_PLUGIN.as_mut() {
619 Some(p) => p,
620 None => return 0,
621 }
622 };
623
624 let c_info = unsafe {
625 &*(window_info_ptr as *const $crate::bindings::miracle_window_info_t)
626 };
627
628 let name = if name_len > 0 {
629 let name_bytes = unsafe {
630 core::slice::from_raw_parts(name_ptr as *const u8, name_len as usize)
631 };
632 String::from_utf8_lossy(name_bytes).into_owned()
633 } else {
634 String::new()
635 };
636
637 let info = unsafe { $crate::window::Window::from_c_with_name(c_info, name) };
638
639 match plugin.place_new_window(&info) {
640 Some(placement) => {
641 let c_placement: $crate::bindings::miracle_placement_t = placement.into();
642 unsafe {
643 let out = &mut *(result_ptr as *mut $crate::bindings::miracle_placement_t);
644 *out = c_placement;
645 }
646 1
647 }
648 None => 0,
649 }
650 }
651
652 #[unsafe(no_mangle)]
653 pub extern "C" fn window_deleted(
654 window_info_ptr: i32,
655 name_ptr: i32,
656 name_len: i32,
657 ) {
658 let plugin = unsafe {
659 match _MIRACLE_PLUGIN.as_mut() {
660 Some(p) => p,
661 None => return,
662 }
663 };
664
665 let c_info = unsafe {
666 &*(window_info_ptr as *const $crate::bindings::miracle_window_info_t)
667 };
668
669 let name = if name_len > 0 {
670 let name_bytes = unsafe {
671 core::slice::from_raw_parts(name_ptr as *const u8, name_len as usize)
672 };
673 String::from_utf8_lossy(name_bytes).into_owned()
674 } else {
675 String::new()
676 };
677
678 let info = unsafe { $crate::window::Window::from_c_with_name(c_info, name) };
679
680 plugin.window_deleted(&info);
681 }
682
683 #[unsafe(no_mangle)]
684 pub extern "C" fn window_focused(
685 window_info_ptr: i32,
686 name_ptr: i32,
687 name_len: i32,
688 ) {
689 let plugin = unsafe {
690 match _MIRACLE_PLUGIN.as_mut() {
691 Some(p) => p,
692 None => return,
693 }
694 };
695
696 let c_info = unsafe {
697 &*(window_info_ptr as *const $crate::bindings::miracle_window_info_t)
698 };
699
700 let name = if name_len > 0 {
701 let name_bytes = unsafe {
702 core::slice::from_raw_parts(name_ptr as *const u8, name_len as usize)
703 };
704 String::from_utf8_lossy(name_bytes).into_owned()
705 } else {
706 String::new()
707 };
708
709 let info = unsafe { $crate::window::Window::from_c_with_name(c_info, name) };
710
711 plugin.window_focused(&info);
712 }
713
714 #[unsafe(no_mangle)]
715 pub extern "C" fn window_unfocused(
716 window_info_ptr: i32,
717 name_ptr: i32,
718 name_len: i32,
719 ) {
720 let plugin = unsafe {
721 match _MIRACLE_PLUGIN.as_mut() {
722 Some(p) => p,
723 None => return,
724 }
725 };
726
727 let c_info = unsafe {
728 &*(window_info_ptr as *const $crate::bindings::miracle_window_info_t)
729 };
730
731 let name = if name_len > 0 {
732 let name_bytes = unsafe {
733 core::slice::from_raw_parts(name_ptr as *const u8, name_len as usize)
734 };
735 String::from_utf8_lossy(name_bytes).into_owned()
736 } else {
737 String::new()
738 };
739
740 let info = unsafe { $crate::window::Window::from_c_with_name(c_info, name) };
741
742 plugin.window_unfocused(&info);
743 }
744
745 #[unsafe(no_mangle)]
746 pub extern "C" fn workspace_created(
747 workspace_info_ptr: i32,
748 name_ptr: i32,
749 name_len: i32,
750 ) {
751 let plugin = unsafe {
752 match _MIRACLE_PLUGIN.as_mut() {
753 Some(p) => p,
754 None => return,
755 }
756 };
757
758 let c_ws = unsafe {
759 &*(workspace_info_ptr as *const $crate::bindings::miracle_workspace_t)
760 };
761
762 let name = if name_len > 0 {
763 let name_bytes = unsafe {
764 core::slice::from_raw_parts(name_ptr as *const u8, name_len as usize)
765 };
766 String::from_utf8_lossy(name_bytes).into_owned()
767 } else {
768 String::new()
769 };
770
771 let ws = unsafe { $crate::workspace::Workspace::from_c_with_name(c_ws, name) };
772 plugin.workspace_created(&ws);
773 }
774
775 #[unsafe(no_mangle)]
776 pub extern "C" fn workspace_removed(
777 workspace_info_ptr: i32,
778 name_ptr: i32,
779 name_len: i32,
780 ) {
781 let plugin = unsafe {
782 match _MIRACLE_PLUGIN.as_mut() {
783 Some(p) => p,
784 None => return,
785 }
786 };
787
788 let c_ws = unsafe {
789 &*(workspace_info_ptr as *const $crate::bindings::miracle_workspace_t)
790 };
791
792 let name = if name_len > 0 {
793 let name_bytes = unsafe {
794 core::slice::from_raw_parts(name_ptr as *const u8, name_len as usize)
795 };
796 String::from_utf8_lossy(name_bytes).into_owned()
797 } else {
798 String::new()
799 };
800
801 let ws = unsafe { $crate::workspace::Workspace::from_c_with_name(c_ws, name) };
802 plugin.workspace_removed(&ws);
803 }
804
805 #[unsafe(no_mangle)]
806 pub extern "C" fn workspace_focused(
807 workspace_info_ptr: i32,
808 name_ptr: i32,
809 name_len: i32,
810 has_previous: i32,
811 previous_id: i64,
812 ) {
813 let plugin = unsafe {
814 match _MIRACLE_PLUGIN.as_mut() {
815 Some(p) => p,
816 None => return,
817 }
818 };
819
820 let c_ws = unsafe {
821 &*(workspace_info_ptr as *const $crate::bindings::miracle_workspace_t)
822 };
823
824 let name = if name_len > 0 {
825 let name_bytes = unsafe {
826 core::slice::from_raw_parts(name_ptr as *const u8, name_len as usize)
827 };
828 String::from_utf8_lossy(name_bytes).into_owned()
829 } else {
830 String::new()
831 };
832
833 let ws = unsafe { $crate::workspace::Workspace::from_c_with_name(c_ws, name) };
834 let prev = if has_previous != 0 { Some(previous_id as u64) } else { None };
835 plugin.workspace_focused(prev, &ws);
836 }
837
838 #[unsafe(no_mangle)]
839 pub extern "C" fn workspace_area_changed(
840 workspace_info_ptr: i32,
841 name_ptr: i32,
842 name_len: i32,
843 ) {
844 let plugin = unsafe {
845 match _MIRACLE_PLUGIN.as_mut() {
846 Some(p) => p,
847 None => return,
848 }
849 };
850
851 let c_ws = unsafe {
852 &*(workspace_info_ptr as *const $crate::bindings::miracle_workspace_t)
853 };
854
855 let name = if name_len > 0 {
856 let name_bytes = unsafe {
857 core::slice::from_raw_parts(name_ptr as *const u8, name_len as usize)
858 };
859 String::from_utf8_lossy(name_bytes).into_owned()
860 } else {
861 String::new()
862 };
863
864 let ws = unsafe { $crate::workspace::Workspace::from_c_with_name(c_ws, name) };
865 plugin.workspace_area_changed(&ws);
866 }
867
868 #[unsafe(no_mangle)]
869 pub extern "C" fn window_workspace_changed(
870 window_info_ptr: i32,
871 window_name_ptr: i32,
872 window_name_len: i32,
873 workspace_info_ptr: i32,
874 workspace_name_ptr: i32,
875 workspace_name_len: i32,
876 ) {
877 let plugin = unsafe {
878 match _MIRACLE_PLUGIN.as_mut() {
879 Some(p) => p,
880 None => return,
881 }
882 };
883
884 let c_info = unsafe {
885 &*(window_info_ptr as *const $crate::bindings::miracle_window_info_t)
886 };
887
888 let window_name = if window_name_len > 0 {
889 let name_bytes = unsafe {
890 core::slice::from_raw_parts(window_name_ptr as *const u8, window_name_len as usize)
891 };
892 String::from_utf8_lossy(name_bytes).into_owned()
893 } else {
894 String::new()
895 };
896
897 let info = unsafe { $crate::window::Window::from_c_with_name(c_info, window_name) };
898
899 let c_ws = unsafe {
900 &*(workspace_info_ptr as *const $crate::bindings::miracle_workspace_t)
901 };
902
903 let workspace_name = if workspace_name_len > 0 {
904 let name_bytes = unsafe {
905 core::slice::from_raw_parts(workspace_name_ptr as *const u8, workspace_name_len as usize)
906 };
907 String::from_utf8_lossy(name_bytes).into_owned()
908 } else {
909 String::new()
910 };
911
912 let ws = unsafe { $crate::workspace::Workspace::from_c_with_name(c_ws, workspace_name) };
913 plugin.window_workspace_changed(&info, &ws);
914 }
915
916 #[unsafe(no_mangle)]
917 pub extern "C" fn configure(buf_ptr: i32, buf_len: i32) -> i32 {
918 let plugin = unsafe {
919 match _MIRACLE_PLUGIN.as_mut() {
920 Some(p) => p,
921 None => return 0,
922 }
923 };
924 $crate::__private::run_configure(plugin, buf_ptr, buf_len)
925 }
926
927 #[unsafe(no_mangle)]
928 pub extern "C" fn handle_keyboard_input(event_ptr: i32) -> i32 {
929 let plugin = unsafe {
930 match _MIRACLE_PLUGIN.as_mut() {
931 Some(p) => p,
932 None => return 0,
933 }
934 };
935
936 let c_event = unsafe {
937 &*(event_ptr as *const $crate::bindings::miracle_keyboard_event_t)
938 };
939
940 let event = $crate::input::KeyboardEvent {
941 action: $crate::input::KeyboardAction::try_from(c_event.action)
942 .unwrap_or_default(),
943 keysym: c_event.keysym,
944 scan_code: c_event.scan_code,
945 modifiers: $crate::input::InputEventModifiers::from(c_event.modifiers),
946 };
947
948 if plugin.handle_keyboard_input(event) { 1 } else { 0 }
949 }
950
951 #[unsafe(no_mangle)]
952 pub extern "C" fn handle_pointer_event(event_ptr: i32) -> i32 {
953 let plugin = unsafe {
954 match _MIRACLE_PLUGIN.as_mut() {
955 Some(p) => p,
956 None => return 0,
957 }
958 };
959
960 let c_event = unsafe {
961 &*(event_ptr as *const $crate::bindings::miracle_pointer_event_t)
962 };
963
964 let event = $crate::input::PointerEvent {
965 x: c_event.x,
966 y: c_event.y,
967 action: $crate::input::PointerAction::try_from(c_event.action)
968 .unwrap_or_default(),
969 modifiers: $crate::input::InputEventModifiers::from(c_event.modifiers),
970 buttons: $crate::input::PointerButtons::from(c_event.buttons),
971 };
972
973 if plugin.handle_pointer_event(event) { 1 } else { 0 }
974 }
975 };
976}