rusteal_core/
delegate_registry.rs1use std::collections::HashMap;
6use std::sync::atomic::{AtomicU64, Ordering};
7use std::sync::{Mutex, OnceLock};
8
9use crate::lock_or_recover;
10
11use rusteal_ffi::{FPropertyHandle, UObjectHandle, RustealErrorCode};
12
13use crate::error::{check_ffi, RustealResult};
14use crate::ffi_dispatch::NativePtr;
15
16type DelegateCallback = Option<Box<dyn FnMut(NativePtr) + Send>>;
17
18static NEXT_ID: AtomicU64 = AtomicU64::new(1);
19static REGISTRY: OnceLock<Mutex<HashMap<u64, DelegateCallback>>> = OnceLock::new();
20
21fn registry() -> &'static Mutex<HashMap<u64, DelegateCallback>> {
22 REGISTRY.get_or_init(|| Mutex::new(HashMap::new()))
23}
24
25pub fn register_callback(f: impl FnMut(NativePtr) + Send + 'static) -> u64 {
27 let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
28 lock_or_recover(registry()).insert(id, Some(Box::new(f)));
29 id
30}
31
32pub fn unregister_callback(id: u64) {
34 lock_or_recover(registry()).remove(&id);
35}
36
37pub fn clear_all() {
40 if let Some(reg) = REGISTRY.get() {
41 lock_or_recover(reg).clear();
42 }
43 NEXT_ID.store(1, Ordering::Relaxed);
44}
45
46pub fn invoke(callback_id: u64, params: NativePtr) {
52 let mut cb = {
54 let mut reg = lock_or_recover(registry());
55 reg.get_mut(&callback_id).and_then(|slot| slot.take())
56 };
57
58 if let Some(ref mut f) = cb {
60 f(params);
61 }
62
63 if let Some(f) = cb {
67 let mut reg = lock_or_recover(registry());
68 if let Some(slot) = reg.get_mut(&callback_id)
69 && slot.is_none()
70 {
71 *slot = Some(f);
72 }
73 }
74}
75
76pub struct DelegateBinding {
82 callback_id: u64,
83 owner: UObjectHandle,
84 prop: FPropertyHandle,
85 is_multicast: bool,
86}
87
88impl DelegateBinding {
89 pub fn new(
91 callback_id: u64,
92 owner: UObjectHandle,
93 prop: FPropertyHandle,
94 is_multicast: bool,
95 ) -> Self {
96 Self {
97 callback_id,
98 owner,
99 prop,
100 is_multicast,
101 }
102 }
103
104 pub fn callback_id(&self) -> u64 {
106 self.callback_id
107 }
108
109 pub fn unbind(self) {
111 }
113
114 pub fn detach(self) {
117 std::mem::forget(self);
118 }
119}
120
121impl Drop for DelegateBinding {
122 fn drop(&mut self) {
123 unregister_callback(self.callback_id);
125
126 if crate::api::is_api_initialized() {
128 unsafe {
129 if self.is_multicast {
130 let _ = crate::ffi_dispatch::delegate_remove_multicast(
131 self.owner,
132 self.prop,
133 self.callback_id,
134 );
135 } else {
136 let _ = crate::ffi_dispatch::delegate_unbind_delegate(self.owner, self.prop);
137 }
138 }
139 }
140 }
141}
142
143pub fn bind_unicast(
149 owner: UObjectHandle,
150 prop: FPropertyHandle,
151 callback: impl FnMut(NativePtr) + Send + 'static,
152) -> RustealResult<DelegateBinding> {
153 let id = register_callback(callback);
154 let result = unsafe { crate::ffi_dispatch::delegate_bind_delegate(owner, prop, id) };
155 if result != RustealErrorCode::Ok {
156 unregister_callback(id);
157 check_ffi(result)?;
158 }
159 Ok(DelegateBinding::new(id, owner, prop, false))
160}
161
162pub fn bind_multicast(
164 owner: UObjectHandle,
165 prop: FPropertyHandle,
166 callback: impl FnMut(NativePtr) + Send + 'static,
167) -> RustealResult<DelegateBinding> {
168 let id = register_callback(callback);
169 let result = unsafe { crate::ffi_dispatch::delegate_add_multicast(owner, prop, id) };
170 if result != RustealErrorCode::Ok {
171 unregister_callback(id);
172 check_ffi(result)?;
173 }
174 Ok(DelegateBinding::new(id, owner, prop, true))
175}