1use std::cmp::Ordering;
2
3#[doc(hidden)]
4#[macro_export]
5macro_rules! make_event_handler {
6 (
7 $(#[$attr:meta])*
8 $event_type:ident,
9 $event_name:ident,
10 $func_type:ident,
11 (
12 $( {
13 name: $name:ident,
14 rust_type: $rust_type:ty,
15 c_type: $c_type:ty,
16 to_rust: $to_rust:expr,
17 }, )*
18 )
19 ) => {
20 paste::item! {
21 #[derive(Debug)]
22 pub struct [<$event_name Event>] {
23 $(pub $name: $rust_type, )*
24 }
25
26 $(#[$attr])*
27 pub struct [<$event_name EventHandler>] {
28 registered: bool,
29 callback_handler: Box<$crate::callback_handler::CallbackHandler<[<$event_name Event>]>>,
30 }
31
32 impl [<$event_name EventHandler>] {
33 #[must_use]
34 pub fn new() -> Self {
35 Self {
36 registered: false,
37 callback_handler: Box::new($crate::callback_handler::CallbackHandler::new()),
38 }
39 }
40
41 pub fn on<F>(&mut self, callback: F)
42 where
43 F: FnMut(&[<$event_name Event>]),
44 F: 'static,
45 {
46 self.callback_handler.on(callback);
47
48 unsafe {
49 self.register_listener();
50 }
51 }
52
53 pub fn raise(
56 $($name: $c_type, )*
57 ) {
58 unsafe {
59 ::classicube_sys::[<Event_Raise $func_type>](
60 &mut ::classicube_sys::[<$event_type Events>].$event_name,
61 $($name, )*
62 );
63 }
64 }
65
66 #[must_use]
67 pub fn index(&self) -> Option<usize> {
68 let event = unsafe { & ::classicube_sys::[<$event_type Events>].$event_name };
69 let ptr: *const $crate::callback_handler::CallbackHandler<[<$event_name Event>]> =
70 self.callback_handler.as_ref();
71 let ptr: *const ::std::os::raw::c_void = ptr.cast();
72
73 #[allow(clippy::cast_sign_loss)]
74 for i in 0..event.Count as usize {
75 let handler = event.Handlers[i];
76 let obj: *const ::std::os::raw::c_void = event.Objs[i].cast();
77 #[allow(clippy::fn_address_comparisons)]
78 if handler
79 .map(|handler| handler == Self::callback)
80 .unwrap_or(false)
81 && obj == ptr
82 {
83 return Some(i);
84 }
85 }
86
87 None
88 }
89
90 #[must_use]
92 pub fn reorder(&self, new_index: usize) -> Option<()> {
93 let old_index = self.index()?;
94
95 let event = unsafe { &mut ::classicube_sys::[<$event_type Events>].$event_name };
96 #[allow(clippy::cast_sign_loss)]
97 let count = event.Count as usize;
98
99 let handlers = &mut event.Handlers[..count];
100 let objs = &mut event.Objs[..count];
101 $crate::macros::reorder(handlers, old_index, new_index);
102 $crate::macros::reorder(objs, old_index, new_index);
103
104 Some(())
105 }
106
107 unsafe fn register_listener(&mut self) {
108 if !self.registered {
109 let handlers = &mut ::classicube_sys::[<$event_type Events>].$event_name;
110 let ptr: *mut $crate::callback_handler::CallbackHandler<[<$event_name Event>]> =
111 self.callback_handler.as_mut();
112
113 ::classicube_sys::[<Event_Register $func_type>](
114 handlers,
115 ptr.cast(),
116 Some(Self::callback),
117 );
118
119 self.registered = true;
120 }
121 }
122
123 unsafe fn unregister_listener(&mut self) {
124 if self.registered {
125 let ptr: *mut $crate::callback_handler::CallbackHandler<[<$event_name Event>]> =
126 self.callback_handler.as_mut();
127
128 ::classicube_sys::[<Event_Unregister $func_type>](
129 &mut ::classicube_sys::[<$event_type Events>].$event_name,
130 ptr.cast(),
131 Some(Self::callback),
132 );
133
134 self.registered = false;
135 }
136 }
137
138 extern "C" fn callback(
139 obj: *mut ::std::os::raw::c_void,
140 $($name: $c_type, )*
141 ) {
142 let event_handler = obj.cast::<$crate::callback_handler::CallbackHandler<[<$event_name Event>]>>();
143 let event_handler = unsafe { &mut *event_handler };
144
145 #[allow(clippy::redundant_closure_call)]
146 let event = [<$event_name Event>] {
147 $($name: ($to_rust)($name), )*
148 };
149
150 $crate::tracing::debug!("{} {:?}", stringify!([<$event_type>]), event);
151
152 event_handler.handle_event(&event);
153 }
154 }
155
156 impl Drop for [<$event_name EventHandler>] {
157 fn drop(&mut self) {
158 unsafe {
159 self.unregister_listener();
160 }
161 }
162 }
163
164 impl Default for [<$event_name EventHandler>] {
165 fn default() -> Self {
166 Self::new()
167 }
168 }
169 }
170 };
171}
172
173pub fn reorder<T>(slice: &mut [T], old_index: usize, new_index: usize) {
174 match new_index.cmp(&old_index) {
175 Ordering::Less => {
176 slice[new_index..=old_index].rotate_right(1);
177 }
178 Ordering::Greater => {
179 slice[old_index..=new_index].rotate_left(1);
180 }
181 Ordering::Equal => {}
182 }
183}
184
185#[test]
186fn test_reorder() {
187 let mut a = [2, 3, 4, 5, 6, 7, 8, 0, 0, 0];
189 let count = 7;
190
191 reorder(&mut a[..count], 5, 2);
193 assert_eq!(&a, &[2, 3, 7, 4, 5, 6, 8, 0, 0, 0]);
195
196 reorder(&mut a[..count], 2, 5);
198 assert_eq!(&a, &[2, 3, 4, 5, 6, 7, 8, 0, 0, 0]);
200
201 reorder(&mut a[..count], 0, 1);
203 assert_eq!(&a, &[3, 2, 4, 5, 6, 7, 8, 0, 0, 0]);
205
206 reorder(&mut a[..count], 0, 0);
208 assert_eq!(&a, &[3, 2, 4, 5, 6, 7, 8, 0, 0, 0]);
210}
211
212#[macro_export]
213macro_rules! time {
214 ($title:tt, $block:block) => {{
215 let before = ::std::time::Instant::now();
216 let res = $block;
217 let after = ::std::time::Instant::now();
218 let diff = after - before;
219 debug!("{} ({:?})", $title, diff);
220 res
221 }};
222
223 ($title:expr, $high_millis:tt, $block:block) => {{
224 let before = ::std::time::Instant::now();
225 let res = $block;
226 let after = ::std::time::Instant::now();
227 let diff = after - before;
228 if diff > ::std::time::Duration::from_millis($high_millis) {
229 $crate::tracing::warn!("{} ({:?})", $title, diff);
230 } else {
231 $crate::tracing::debug!("{} ({:?})", $title, diff);
232 }
233 res
234 }};
235}
236
237#[macro_export]
238macro_rules! time_silent {
239 ($title:expr, $high_millis:tt, $block:block) => {{
240 let before = ::std::time::Instant::now();
241 let res = $block;
242 let after = ::std::time::Instant::now();
243 let diff = after - before;
244 if diff > ::std::time::Duration::from_millis($high_millis) {
245 $crate::tracing::warn!("{} ({:?})", $title, diff);
246 }
247 res
248 }};
249}
250
251#[macro_export]
252macro_rules! test_noop_fn {
253 ($name:tt) => {
254 #[cfg(test)]
255 #[no_mangle]
256 pub extern "C" fn $name() {}
257 };
258}
259
260#[macro_export]
261macro_rules! test_noop_static {
262 ($name:tt) => {
263 #[cfg(test)]
264 #[no_mangle]
265 pub static mut $name: () = ();
266 };
267}