use crate::*;
use std::ffi::CString;
use std::marker::PhantomData;
use std::ptr;
use crate::core_types::{GodotString, Variant};
use crate::nativescript::NativeClass;
use crate::nativescript::NativeClassMethods;
use crate::nativescript::UserData;
use crate::private::get_api;
use super::emplace;
pub mod property;
pub use self::property::{Export, ExportInfo, PropertyBuilder, Usage as PropertyUsage};
#[derive(Copy, Clone)]
pub struct InitHandle {
#[doc(hidden)]
handle: *mut libc::c_void,
}
impl InitHandle {
#[doc(hidden)]
#[inline]
pub unsafe fn new(handle: *mut libc::c_void) -> Self {
InitHandle { handle }
}
#[inline]
pub fn add_class<C>(self)
where
C: NativeClassMethods,
{
self.add_maybe_tool_class::<C>(false)
}
#[inline]
pub fn add_tool_class<C>(self)
where
C: NativeClassMethods,
{
self.add_maybe_tool_class::<C>(true)
}
#[inline]
fn add_maybe_tool_class<C>(self, is_tool: bool)
where
C: NativeClassMethods,
{
unsafe {
let class_name = CString::new(C::class_name()).unwrap();
let base_name = CString::new(C::Base::class_name()).unwrap();
let create = {
unsafe extern "C" fn constructor<C: NativeClass>(
this: *mut sys::godot_object,
_method_data: *mut libc::c_void,
) -> *mut libc::c_void {
use std::panic::{self, AssertUnwindSafe};
let this = match ptr::NonNull::new(this) {
Some(this) => this,
None => {
godot_error!(
"gdnative-core: error constructing {}: owner pointer is null",
C::class_name(),
);
return ptr::null_mut();
}
};
let owner = match object::RawObject::<C::Base>::try_from_sys_ref(this) {
Some(owner) => owner,
None => {
godot_error!(
"gdnative-core: error constructing {}: incompatible owner type, expecting {}",
C::class_name(),
C::Base::class_name(),
);
return ptr::null_mut();
}
};
let val = match panic::catch_unwind(AssertUnwindSafe(|| {
emplace::take()
.unwrap_or_else(|| C::init(TRef::new(C::Base::cast_ref(owner))))
})) {
Ok(val) => val,
Err(_) => {
godot_error!(
"gdnative-core: error constructing {}: constructor panicked",
C::class_name(),
);
return ptr::null_mut();
}
};
let wrapper = C::UserData::new(val);
C::UserData::into_user_data(wrapper) as *mut _
}
sys::godot_instance_create_func {
create_func: Some(constructor::<C>),
method_data: ptr::null_mut(),
free_func: None,
}
};
let destroy = {
unsafe extern "C" fn destructor<C: NativeClass>(
_this: *mut sys::godot_object,
_method_data: *mut libc::c_void,
user_data: *mut libc::c_void,
) {
if user_data.is_null() {
godot_error!(
"gdnative-core: user data pointer for {} is null (did the constructor fail?)",
C::class_name(),
);
return;
}
let wrapper = C::UserData::consume_user_data_unchecked(user_data);
drop(wrapper)
}
sys::godot_instance_destroy_func {
destroy_func: Some(destructor::<C>),
method_data: ptr::null_mut(),
free_func: None,
}
};
if is_tool {
(get_api().godot_nativescript_register_tool_class)(
self.handle as *mut _,
class_name.as_ptr() as *const _,
base_name.as_ptr() as *const _,
create,
destroy,
);
} else {
(get_api().godot_nativescript_register_class)(
self.handle as *mut _,
class_name.as_ptr() as *const _,
base_name.as_ptr() as *const _,
create,
destroy,
);
}
(get_api().godot_nativescript_set_type_tag)(
self.handle as *mut _,
class_name.as_ptr() as *const _,
crate::nativescript::type_tag::create::<C>(),
);
let builder = ClassBuilder {
init_handle: self.handle,
class_name,
_marker: PhantomData,
};
C::register_properties(&builder);
C::register(&builder);
}
}
}
pub type ScriptMethodFn = unsafe extern "C" fn(
*mut sys::godot_object,
*mut libc::c_void,
*mut libc::c_void,
libc::c_int,
*mut *mut sys::godot_variant,
) -> sys::godot_variant;
pub enum RpcMode {
Disabled,
Remote,
RemoteSync,
Master,
Puppet,
MasterSync,
PuppetSync,
}
pub struct ScriptMethodAttributes {
pub rpc_mode: RpcMode,
}
pub struct ScriptMethod<'l> {
pub name: &'l str,
pub method_ptr: Option<ScriptMethodFn>,
pub attributes: ScriptMethodAttributes,
pub method_data: *mut libc::c_void,
pub free_func: Option<unsafe extern "C" fn(*mut libc::c_void) -> ()>,
}
#[derive(Debug)]
pub struct ClassBuilder<C> {
init_handle: *mut libc::c_void,
class_name: CString,
_marker: PhantomData<C>,
}
impl<C: NativeClass> ClassBuilder<C> {
#[inline]
pub fn add_method_advanced(&self, method: ScriptMethod) {
let method_name = CString::new(method.name).unwrap();
let rpc = match method.attributes.rpc_mode {
RpcMode::Master => sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_MASTER,
RpcMode::Remote => sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_REMOTE,
RpcMode::Puppet => sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_PUPPET,
RpcMode::RemoteSync => sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_REMOTESYNC,
RpcMode::Disabled => sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_DISABLED,
RpcMode::MasterSync => sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_MASTERSYNC,
RpcMode::PuppetSync => sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_PUPPETSYNC,
};
let attr = sys::godot_method_attributes { rpc_type: rpc };
let method_desc = sys::godot_instance_method {
method: method.method_ptr,
method_data: method.method_data,
free_func: method.free_func,
};
unsafe {
(get_api().godot_nativescript_register_method)(
self.init_handle,
self.class_name.as_ptr() as *const _,
method_name.as_ptr() as *const _,
attr,
method_desc,
);
}
}
#[inline]
pub fn add_method_with_rpc_mode(&self, name: &str, method: ScriptMethodFn, rpc_mode: RpcMode) {
self.add_method_advanced(ScriptMethod {
name,
method_ptr: Some(method),
attributes: ScriptMethodAttributes { rpc_mode },
method_data: ptr::null_mut(),
free_func: None,
});
}
#[inline]
pub fn add_method(&self, name: &str, method: ScriptMethodFn) {
self.add_method_with_rpc_mode(name, method, RpcMode::Disabled);
}
#[inline]
pub fn add_property<'a, T>(&'a self, name: &'a str) -> PropertyBuilder<'a, C, T>
where
T: Export,
{
PropertyBuilder::new(self, name)
}
#[inline]
pub fn add_signal(&self, signal: Signal) {
unsafe {
let name = GodotString::from_str(signal.name);
let owned = signal
.args
.iter()
.map(|arg| {
let arg_name = GodotString::from_str(arg.name);
let hint_string = arg.export_info.hint_string.new_ref();
(arg, arg_name, hint_string)
})
.collect::<Vec<_>>();
let mut args = owned
.iter()
.map(|(arg, arg_name, hint_string)| sys::godot_signal_argument {
name: arg_name.to_sys(),
type_: arg.default.get_type() as i32,
hint: arg.export_info.hint_kind,
hint_string: hint_string.to_sys(),
usage: arg.usage.to_sys(),
default_value: arg.default.to_sys(),
})
.collect::<Vec<_>>();
(get_api().godot_nativescript_register_signal)(
self.init_handle,
self.class_name.as_ptr(),
&sys::godot_signal {
name: name.to_sys(),
num_args: args.len() as i32,
args: args.as_mut_ptr(),
num_default_args: 0,
default_args: ptr::null_mut(),
},
);
}
}
}
pub struct Signal<'l> {
pub name: &'l str,
pub args: &'l [SignalArgument<'l>],
}
pub struct SignalArgument<'l> {
pub name: &'l str,
pub default: Variant,
pub export_info: ExportInfo,
pub usage: PropertyUsage,
}