uika_runtime/
delegate_registry.rs1use std::collections::HashMap;
6use std::sync::atomic::{AtomicU64, Ordering};
7use std::sync::{Mutex, OnceLock};
8
9use uika_ffi::{FPropertyHandle, UObjectHandle, UikaErrorCode};
10
11use crate::error::{check_ffi, UikaResult};
12
13type DelegateCallback = Option<Box<dyn FnMut(*mut u8) + Send>>;
14
15static NEXT_ID: AtomicU64 = AtomicU64::new(1);
16static REGISTRY: OnceLock<Mutex<HashMap<u64, DelegateCallback>>> = OnceLock::new();
17
18fn registry() -> &'static Mutex<HashMap<u64, DelegateCallback>> {
19 REGISTRY.get_or_init(|| Mutex::new(HashMap::new()))
20}
21
22pub fn register_callback(f: impl FnMut(*mut u8) + Send + 'static) -> u64 {
24 let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
25 registry().lock().unwrap().insert(id, Some(Box::new(f)));
26 id
27}
28
29pub fn unregister_callback(id: u64) {
31 registry().lock().unwrap().remove(&id);
32}
33
34pub fn clear_all() {
37 if let Some(reg) = REGISTRY.get() {
38 reg.lock().unwrap().clear();
39 }
40 NEXT_ID.store(1, Ordering::Relaxed);
41}
42
43pub fn invoke(callback_id: u64, params: *mut u8) {
49 let mut cb = {
51 let mut reg = registry().lock().unwrap();
52 reg.get_mut(&callback_id).and_then(|slot| slot.take())
53 };
54
55 if let Some(ref mut f) = cb {
57 f(params);
58 }
59
60 if let Some(f) = cb {
64 let mut reg = registry().lock().unwrap();
65 if let Some(slot) = reg.get_mut(&callback_id) {
66 if slot.is_none() {
67 *slot = Some(f);
68 }
69 }
70 }
71}
72
73pub struct DelegateBinding {
79 callback_id: u64,
80 owner: UObjectHandle,
81 prop: FPropertyHandle,
82 is_multicast: bool,
83}
84
85impl DelegateBinding {
86 pub fn new(
88 callback_id: u64,
89 owner: UObjectHandle,
90 prop: FPropertyHandle,
91 is_multicast: bool,
92 ) -> Self {
93 Self {
94 callback_id,
95 owner,
96 prop,
97 is_multicast,
98 }
99 }
100
101 pub fn callback_id(&self) -> u64 {
103 self.callback_id
104 }
105
106 pub fn unbind(self) {
108 }
110}
111
112impl Drop for DelegateBinding {
113 fn drop(&mut self) {
114 unregister_callback(self.callback_id);
116
117 unsafe {
119 let api = crate::api::api();
120 if !api.delegate.is_null() {
121 if self.is_multicast {
122 let _ = ((*api.delegate).remove_multicast)(
123 self.owner,
124 self.prop,
125 self.callback_id,
126 );
127 } else {
128 let _ = ((*api.delegate).unbind_delegate)(self.owner, self.prop);
129 }
130 }
131 }
132 }
133}
134
135pub fn bind_unicast(
141 owner: UObjectHandle,
142 prop: FPropertyHandle,
143 callback: impl FnMut(*mut u8) + Send + 'static,
144) -> UikaResult<DelegateBinding> {
145 let id = register_callback(callback);
146 let result = unsafe { ((*crate::api::api().delegate).bind_delegate)(owner, prop, id) };
147 if result != UikaErrorCode::Ok {
148 unregister_callback(id);
149 check_ffi(result)?;
150 }
151 Ok(DelegateBinding::new(id, owner, prop, false))
152}
153
154pub fn bind_multicast(
156 owner: UObjectHandle,
157 prop: FPropertyHandle,
158 callback: impl FnMut(*mut u8) + Send + 'static,
159) -> UikaResult<DelegateBinding> {
160 let id = register_callback(callback);
161 let result = unsafe { ((*crate::api::api().delegate).add_multicast)(owner, prop, id) };
162 if result != UikaErrorCode::Ok {
163 unregister_callback(id);
164 check_ffi(result)?;
165 }
166 Ok(DelegateBinding::new(id, owner, prop, true))
167}