aboutsummaryrefslogtreecommitdiff
path: root/src/callback.rs
diff options
context:
space:
mode:
authorScetch <[email protected]>2018-03-10 14:27:22 -0500
committerMatthew Collins <[email protected]>2018-03-16 21:33:25 +0000
commite3f9fbbbd057713cad6921e45a95200a7e86ab02 (patch)
tree1b386cbb6cd3610980f3cb295cd307f182545d47 /src/callback.rs
parentReplace Cow<str> with String in the API (diff)
downloadsteamworks-rs-e3f9fbbbd057713cad6921e45a95200a7e86ab02.tar.xz
steamworks-rs-e3f9fbbbd057713cad6921e45a95200a7e86ab02.zip
Cleanup callback handling.
Diffstat (limited to 'src/callback.rs')
-rw-r--r--src/callback.rs126
1 files changed, 126 insertions, 0 deletions
diff --git a/src/callback.rs b/src/callback.rs
new file mode 100644
index 0000000..d249207
--- /dev/null
+++ b/src/callback.rs
@@ -0,0 +1,126 @@
+use super::*;
+
+use libc::{ c_void };
+use sys;
+
+use std::mem;
+use std::sync::{ Arc, Weak };
+
+pub unsafe trait Callback {
+ const ID: i32;
+ const SIZE: i32;
+ unsafe fn from_raw(raw: *mut c_void) -> Self;
+}
+
+pub(crate) unsafe fn register_callback<C, F, Manager>(inner: &Arc<Inner<Manager>>, f: F, game_server: bool)
+ where C: Callback,
+ F: FnMut(C) + Send + Sync + 'static
+{
+ unsafe extern "C" fn run<C, F>(_: *mut c_void, userdata: *mut c_void, param: *mut c_void)
+ where C: Callback,
+ F: FnMut(C) + Send + Sync + 'static
+ {
+ let func: &mut F = &mut *(userdata as *mut F);
+ let param = C::from_raw(param);
+ func(param);
+ }
+
+ unsafe extern "C" fn run_extra<C, F>(cb: *mut c_void, userdata: *mut c_void, param: *mut c_void, _: bool, _: sys::SteamAPICall)
+ where C: Callback,
+ F: FnMut(C) + Send + Sync + 'static
+ {
+ run::<C, F>(cb, userdata, param);
+ }
+
+ unsafe extern "C" fn dealloc<C, F>(cb: *mut c_void, userdata: *mut c_void)
+ where C: Callback,
+ F: FnMut(C) + Send + Sync + 'static
+ {
+ sys::SteamAPI_UnregisterCallback(cb);
+
+ let func: Box<F> = Box::from_raw(userdata as _);
+ drop(func);
+ }
+
+ let data = sys::CallbackData {
+ param_size: C::SIZE as _,
+ userdata: Box::into_raw(Box::new(f)) as _,
+ run: run::<C, F> as _,
+ run_extra: run_extra::<C, F> as _,
+ dealloc: dealloc::<C, F> as _,
+ };
+
+ let flags = if game_server { 0x02 } else { 0x00 };
+ let ptr = sys::create_rust_callback(flags, C::ID as _, data);
+
+ sys::SteamAPI_RegisterCallback(ptr, C::ID as _);
+
+ let mut cbs = inner.callbacks.lock().unwrap();
+ cbs.callbacks.push(ptr);
+}
+
+pub(crate) unsafe fn register_call_result<C, F, Manager>(inner: &Arc<Inner<Manager>>, api_call: sys::SteamAPICall, callback_id: i32, f: F)
+ where F: for <'a> FnMut(&'a C, bool) + 'static + Send + Sync
+{
+ struct CallData<F, Manager> {
+ func: F,
+ api_call: sys::SteamAPICall,
+ inner: Weak<Inner<Manager>>,
+ }
+
+ unsafe extern "C" fn run<C, F, Manager>(cb: *mut c_void, userdata: *mut c_void, param: *mut c_void)
+ where F: for<'a> FnMut(&'a C, bool) + Send + Sync + 'static
+ {
+ let data: &mut CallData<F, Manager> = &mut *(userdata as *mut CallData<F, Manager>);
+ (data.func)(&*(param as *const _), false);
+
+ sys::delete_rust_callback(cb);
+ }
+
+ unsafe extern "C" fn run_extra<C, F, Manager>(cb: *mut c_void, userdata: *mut c_void, param: *mut c_void, io_error: bool, api_call: sys::SteamAPICall)
+ where F: for<'a> FnMut(&'a C, bool) + Send + Sync + 'static
+ {
+ let data: &mut CallData<F, Manager> = &mut *(userdata as *mut CallData<F, Manager>);
+
+ if api_call == data.api_call {
+ (data.func)(&*(param as *const _), io_error);
+ sys::delete_rust_callback(cb);
+ }
+ }
+
+ unsafe extern "C" fn dealloc<C, F, Manager>(cb: *mut c_void, userdata: *mut c_void)
+ where F: for <'a> FnMut(&'a C, bool) + Send + Sync + 'static
+ {
+ let data: Box<CallData<F, Manager>> = Box::from_raw(userdata as _);
+
+ if let Some(inner) = data.inner.upgrade() {
+ sys::SteamAPI_UnregisterCallResult(cb, data.api_call);
+
+ let mut cbs = inner.callbacks.lock().unwrap();
+ cbs.call_results.remove(&data.api_call);
+ }
+
+ drop(data);
+ }
+
+ let userdata = CallData {
+ func: f,
+ api_call: api_call,
+ inner: Arc::downgrade(&inner),
+ };
+
+ let data = sys::CallbackData {
+ param_size: mem::size_of::<C> as _,
+ userdata: Box::into_raw(Box::new(userdata)) as _,
+ run: run::<C, F, Manager>,
+ run_extra: run_extra::<C, F, Manager>,
+ dealloc: dealloc::<C, F, Manager>,
+ };
+
+ let ptr = sys::create_rust_callback(0x00, callback_id, data);
+
+ sys::SteamAPI_RegisterCallResult(ptr, api_call);
+
+ let mut cbs = inner.callbacks.lock().unwrap();
+ cbs.call_results.insert(api_call, ptr);
+} \ No newline at end of file