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
115impl Drop for DelegateBinding {
116 fn drop(&mut self) {
117 unregister_callback(self.callback_id);
119
120 if crate::api::is_api_initialized() {
122 unsafe {
123 if self.is_multicast {
124 let _ = crate::ffi_dispatch::delegate_remove_multicast(
125 self.owner,
126 self.prop,
127 self.callback_id,
128 );
129 } else {
130 let _ = crate::ffi_dispatch::delegate_unbind_delegate(self.owner, self.prop);
131 }
132 }
133 }
134 }
135}
136
137pub fn bind_unicast(
143 owner: UObjectHandle,
144 prop: FPropertyHandle,
145 callback: impl FnMut(NativePtr) + Send + 'static,
146) -> RustealResult<DelegateBinding> {
147 let id = register_callback(callback);
148 let result = unsafe { crate::ffi_dispatch::delegate_bind_delegate(owner, prop, id) };
149 if result != RustealErrorCode::Ok {
150 unregister_callback(id);
151 check_ffi(result)?;
152 }
153 Ok(DelegateBinding::new(id, owner, prop, false))
154}
155
156pub fn bind_multicast(
158 owner: UObjectHandle,
159 prop: FPropertyHandle,
160 callback: impl FnMut(NativePtr) + Send + 'static,
161) -> RustealResult<DelegateBinding> {
162 let id = register_callback(callback);
163 let result = unsafe { crate::ffi_dispatch::delegate_add_multicast(owner, prop, id) };
164 if result != RustealErrorCode::Ok {
165 unregister_callback(id);
166 check_ffi(result)?;
167 }
168 Ok(DelegateBinding::new(id, owner, prop, true))
169}