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 *&raw 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 { &*&raw const ::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(unpredictable_function_pointer_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 *&raw 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 = unsafe { &mut *&raw 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 unsafe {
114 ::classicube_sys::[<Event_Register $func_type>](
115 handlers,
116 ptr.cast(),
117 Some(Self::callback),
118 );
119 }
120
121 self.registered = true;
122 }
123 }
124
125 unsafe fn unregister_listener(&mut self) {
126 if self.registered {
127 let ptr: *mut $crate::callback_handler::CallbackHandler<[<$event_name Event>]> =
128 self.callback_handler.as_mut();
129
130 unsafe {
131 ::classicube_sys::[<Event_Unregister $func_type>](
132 &mut *&raw mut ::classicube_sys::[<$event_type Events>].$event_name,
133 ptr.cast(),
134 Some(Self::callback),
135 );
136 }
137
138 self.registered = false;
139 }
140 }
141
142 extern "C" fn callback(
143 obj: *mut ::std::os::raw::c_void,
144 $($name: $c_type, )*
145 ) {
146 let event_handler = obj.cast::<$crate::callback_handler::CallbackHandler<[<$event_name Event>]>>();
147 let event_handler = unsafe { &mut *event_handler };
148
149 #[allow(clippy::redundant_closure_call)]
150 let event = [<$event_name Event>] {
151 $($name: ($to_rust)($name), )*
152 };
153
154 $crate::tracing::debug!("{} {:?}", stringify!([<$event_type>]), event);
155
156 event_handler.handle_event(&event);
157 }
158 }
159
160 impl Drop for [<$event_name EventHandler>] {
161 fn drop(&mut self) {
162 unsafe {
163 self.unregister_listener();
164 }
165 }
166 }
167
168 impl Default for [<$event_name EventHandler>] {
169 fn default() -> Self {
170 Self::new()
171 }
172 }
173 }
174 };
175}
176
177pub fn reorder<T>(slice: &mut [T], old_index: usize, new_index: usize) {
178 match new_index.cmp(&old_index) {
179 Ordering::Less => {
180 slice[new_index..=old_index].rotate_right(1);
181 }
182 Ordering::Greater => {
183 slice[old_index..=new_index].rotate_left(1);
184 }
185 Ordering::Equal => {}
186 }
187}
188
189#[test]
190fn test_reorder() {
191 let mut a = [2, 3, 4, 5, 6, 7, 8, 0, 0, 0];
193 let count = 7;
194
195 reorder(&mut a[..count], 5, 2);
197 assert_eq!(&a, &[2, 3, 7, 4, 5, 6, 8, 0, 0, 0]);
199
200 reorder(&mut a[..count], 2, 5);
202 assert_eq!(&a, &[2, 3, 4, 5, 6, 7, 8, 0, 0, 0]);
204
205 reorder(&mut a[..count], 0, 1);
207 assert_eq!(&a, &[3, 2, 4, 5, 6, 7, 8, 0, 0, 0]);
209
210 reorder(&mut a[..count], 0, 0);
212 assert_eq!(&a, &[3, 2, 4, 5, 6, 7, 8, 0, 0, 0]);
214}
215
216#[macro_export]
217macro_rules! time {
218 ($title:tt, $block:block) => {{
219 let before = ::std::time::Instant::now();
220 let res = $block;
221 let after = ::std::time::Instant::now();
222 let diff = after - before;
223 debug!("{} ({:?})", $title, diff);
224 res
225 }};
226
227 ($title:expr, $high_millis:tt, $block:block) => {{
228 let before = ::std::time::Instant::now();
229 let res = $block;
230 let after = ::std::time::Instant::now();
231 let diff = after - before;
232 if diff > ::std::time::Duration::from_millis($high_millis) {
233 $crate::tracing::warn!("{} ({:?})", $title, diff);
234 } else {
235 $crate::tracing::debug!("{} ({:?})", $title, diff);
236 }
237 res
238 }};
239}
240
241#[macro_export]
242macro_rules! time_silent {
243 ($title:expr, $high_millis:tt, $block:block) => {{
244 let before = ::std::time::Instant::now();
245 let res = $block;
246 let after = ::std::time::Instant::now();
247 let diff = after - before;
248 if diff > ::std::time::Duration::from_millis($high_millis) {
249 $crate::tracing::warn!("{} ({:?})", $title, diff);
250 }
251 res
252 }};
253}
254
255#[macro_export]
256macro_rules! test_noop_fn {
257 ($name:tt) => {
258 #[cfg(test)]
259 #[allow(non_snake_case)]
260 #[unsafe(no_mangle)]
261 pub extern "C" fn $name() {}
262 };
263}
264
265#[macro_export]
266macro_rules! test_noop_static {
267 ($name:tt) => {
268 #[cfg(test)]
269 #[allow(non_upper_case_globals)]
270 #[unsafe(no_mangle)]
271 pub static mut $name: () = ();
272 };
273}