use super::*;
use crate::get_api;
use crate::FromVariant;
use crate::Map;
use crate::MapMut;
use crate::NativeClass;
use crate::ToVariant;
use crate::Variant;
use libc;
use std::ffi::CString;
use std::marker::PhantomData;
use std::mem;
use std::ops::Range;
use std::ptr;
#[derive(Copy, Clone)]
pub struct InitHandle {
#[doc(hidden)]
handle: *mut libc::c_void,
}
impl InitHandle {
#[doc(hidden)]
pub unsafe fn new(handle: *mut libc::c_void) -> Self {
InitHandle { handle }
}
pub fn add_class<C>(&self)
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 {
let val = C::init(C::Base::from_sys(this));
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,
) -> () {
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,
}
};
(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::type_tag::create::<C>(),
);
let mut builder = ClassBuilder {
init_handle: self.handle,
class_name,
_marker: PhantomData,
};
C::register_properties(&mut builder);
C::register(&mut builder);
}
}
pub fn add_tool_class<C>(&self)
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 {
let val = C::init(C::Base::from_sys(this));
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,
) -> () {
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,
}
};
(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,
);
(get_api().godot_nativescript_set_type_tag)(
self.handle as *mut _,
class_name.as_ptr() as *const _,
crate::type_tag::create::<C>(),
);
let mut builder = ClassBuilder {
init_handle: self.handle,
class_name,
_marker: PhantomData,
};
C::register_properties(&mut builder);
C::register(&mut 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 type ScriptConstructorFn =
unsafe extern "C" fn(*mut sys::godot_object, *mut libc::c_void) -> *mut libc::c_void;
pub type ScriptDestructorFn =
unsafe extern "C" fn(*mut sys::godot_object, *mut libc::c_void, *mut libc::c_void) -> ();
pub enum RpcMode {
Disabled,
Remote,
Sync,
Mater,
Slave,
}
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) -> ()>,
}
pub struct ClassDescriptor<'l> {
pub name: &'l str,
pub base_class: &'l str,
pub constructor: Option<ScriptConstructorFn>,
pub destructor: Option<ScriptDestructorFn>,
}
pub struct ClassBuilder<C: NativeClass> {
#[doc(hidden)]
pub init_handle: *mut libc::c_void,
class_name: CString,
_marker: PhantomData<C>,
}
impl<C: NativeClass> ClassBuilder<C> {
pub fn add_method_advanced(&self, method: ScriptMethod) {
let method_name = CString::new(method.name).unwrap();
let attr = sys::godot_method_attributes {
rpc_type: sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_DISABLED,
};
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,
);
}
}
pub fn add_method(&self, name: &str, method: ScriptMethodFn) {
self.add_method_advanced(ScriptMethod {
name: name,
method_ptr: Some(method),
attributes: ScriptMethodAttributes {
rpc_mode: RpcMode::Disabled,
},
method_data: ptr::null_mut(),
free_func: None,
});
}
pub fn add_property<T, S, G>(&self, property: Property<T, S, G>)
where
T: ToVariant + FromVariant,
S: PropertySetter<C, T>,
G: PropertyGetter<C, T>,
{
unsafe {
let hint_text = match property.hint {
PropertyHint::Range {
ref range,
step,
slider,
} => {
if slider {
Some(format!("{},{},{},slider", range.start, range.end, step))
} else {
Some(format!("{},{},{}", range.start, range.end, step))
}
}
PropertyHint::Enum { values } | PropertyHint::Flags { values } => {
Some(values.join(","))
}
PropertyHint::NodePathToEditedNode | PropertyHint::None => None,
};
let hint_string = if let Some(text) = hint_text {
GodotString::from_str(text)
} else {
GodotString::default()
};
let default: Variant = property.default.to_variant();
let ty = default.get_type();
let mut attr = sys::godot_property_attributes {
rset_type: sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_DISABLED, type_: mem::transmute(ty),
hint: property.hint.to_sys(),
hint_string: hint_string.to_sys(),
usage: property.usage.to_sys(),
default_value: default.to_sys(),
};
let path = ::std::ffi::CString::new(property.name).unwrap();
let set = property.setter.as_godot_function();
let get = property.getter.as_godot_function();
(get_api().godot_nativescript_register_property)(
self.init_handle,
self.class_name.as_ptr(),
path.as_ptr() as *const _,
&mut attr,
set,
get,
);
}
}
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 = GodotString::new();
(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.hint.to_sys(),
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 enum PropertyHint<'l> {
None,
Range {
range: Range<f64>,
step: f64,
slider: bool,
},
Enum {
values: &'l [&'l str],
},
Flags {
values: &'l [&'l str],
},
NodePathToEditedNode,
}
impl<'l> PropertyHint<'l> {
pub fn to_sys(&self) -> sys::godot_property_hint {
match *self {
PropertyHint::None => sys::godot_property_hint_GODOT_PROPERTY_HINT_NONE,
PropertyHint::Range { .. } => sys::godot_property_hint_GODOT_PROPERTY_HINT_RANGE,
PropertyHint::Enum { .. } => sys::godot_property_hint_GODOT_PROPERTY_HINT_ENUM,
PropertyHint::Flags { .. } => sys::godot_property_hint_GODOT_PROPERTY_HINT_FLAGS,
PropertyHint::NodePathToEditedNode => {
sys::godot_property_hint_GODOT_PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE
}
}
}
}
bitflags! {
pub struct PropertyUsage: u32 {
const STORAGE = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_STORAGE as u32;
const EDITOR = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_EDITOR as u32;
const NETWORK = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_NETWORK as u32;
const EDITOR_HELPER = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_EDITOR_HELPER as u32;
const CHECKABLE = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_CHECKABLE as u32;
const CHECKED = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_CHECKED as u32;
const INTERNATIONALIZED = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_INTERNATIONALIZED as u32;
const GROUP = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_GROUP as u32;
const CATEGORY = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_CATEGORY as u32;
const STORE_IF_NONZERO = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_STORE_IF_NONZERO as u32;
const STORE_IF_NONONE = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_STORE_IF_NONONE as u32;
const NO_INSTANCE_STATE = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_NO_INSTANCE_STATE as u32;
const RESTART_IF_CHANGED = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_RESTART_IF_CHANGED as u32;
const SCRIPT_VARIABLE = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_SCRIPT_VARIABLE as u32;
const STORE_IF_NULL = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_STORE_IF_NULL as u32;
const ANIMATE_AS_TRIGGER = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_ANIMATE_AS_TRIGGER as u32;
const UPDATE_ALL_IF_MODIFIED = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED as u32;
const DEFAULT = Self::STORAGE.bits | Self::EDITOR.bits | Self::NETWORK.bits as u32;
const DEFAULT_INTL = Self::DEFAULT.bits | Self::INTERNATIONALIZED.bits as u32;
const NOEDITOR = Self::STORAGE.bits | Self::NETWORK.bits as u32;
}
}
impl PropertyUsage {
pub fn to_sys(&self) -> sys::godot_property_usage_flags {
unsafe { mem::transmute(*self) }
}
}
pub struct Property<'l, T, S, G> {
pub name: &'l str,
pub setter: S,
pub getter: G,
pub default: T,
pub hint: PropertyHint<'l>,
pub usage: PropertyUsage,
}
pub struct SignalArgument<'l> {
pub name: &'l str,
pub default: Variant,
pub hint: PropertyHint<'l>,
pub usage: PropertyUsage,
}
pub struct Signal<'l> {
pub name: &'l str,
pub args: &'l [SignalArgument<'l>],
}
pub unsafe trait PropertySetter<C: NativeClass, T: FromVariant> {
unsafe fn as_godot_function(self) -> sys::godot_property_set_func;
}
pub unsafe trait PropertyGetter<C: NativeClass, T: ToVariant> {
unsafe fn as_godot_function(self) -> sys::godot_property_get_func;
}
extern "C" fn empty_setter(
_this: *mut sys::godot_object,
_method: *mut libc::c_void,
_class: *mut libc::c_void,
_val: *mut sys::godot_variant,
) {
}
extern "C" fn empty_getter(
_this: *mut sys::godot_object,
_method: *mut libc::c_void,
_class: *mut libc::c_void,
) -> sys::godot_variant {
Variant::new().forget()
}
extern "C" fn empty_free_func(_data: *mut libc::c_void) {}
unsafe impl<C: NativeClass, T: FromVariant> PropertySetter<C, T> for () {
unsafe fn as_godot_function(self) -> sys::godot_property_set_func {
let mut set = sys::godot_property_set_func::default();
set.set_func = Some(empty_setter);
set.free_func = Some(empty_free_func);
set
}
}
unsafe impl<C: NativeClass, T: ToVariant> PropertyGetter<C, T> for () {
unsafe fn as_godot_function(self) -> sys::godot_property_get_func {
let mut get = sys::godot_property_get_func::default();
get.get_func = Some(empty_getter);
get.free_func = Some(empty_free_func);
get
}
}
unsafe impl<F, C, T> PropertySetter<C, T> for F
where
C: NativeClass,
C::UserData: MapMut,
T: FromVariant,
F: Fn(&mut C, T),
{
unsafe fn as_godot_function(self) -> sys::godot_property_set_func {
let mut set = sys::godot_property_set_func::default();
let data = Box::new(self);
set.method_data = Box::into_raw(data) as *mut _;
extern "C" fn invoke<C, F, T>(
_this: *mut sys::godot_object,
method: *mut libc::c_void,
class: *mut libc::c_void,
val: *mut sys::godot_variant,
) where
C: NativeClass,
C::UserData: MapMut,
T: FromVariant,
F: Fn(&mut C, T),
{
unsafe {
let rust_ty = C::UserData::clone_from_user_data_unchecked(class as *const _);
let func = &mut *(method as *mut F);
if let Some(val) = T::from_variant(Variant::cast_ref(val)) {
if let Err(err) = rust_ty.map_mut(|rust_ty| func(rust_ty, val)) {
godot_error!("gdnative-core: cannot call property setter: {:?}", err);
}
} else {
godot_error!("Incorrect type passed to property");
}
}
}
set.set_func = Some(invoke::<C, F, T>);
extern "C" fn free_func<F>(data: *mut libc::c_void) {
unsafe {
drop(Box::from_raw(data as *mut F));
}
}
set.free_func = Some(free_func::<F>);
set
}
}
unsafe impl<F, C, T> PropertyGetter<C, T> for F
where
C: NativeClass,
C::UserData: Map,
T: ToVariant,
F: Fn(&C) -> T,
{
unsafe fn as_godot_function(self) -> sys::godot_property_get_func {
let mut get = sys::godot_property_get_func::default();
let data = Box::new(self);
get.method_data = Box::into_raw(data) as *mut _;
extern "C" fn invoke<C, F, T>(
_this: *mut sys::godot_object,
method: *mut libc::c_void,
class: *mut libc::c_void,
) -> sys::godot_variant
where
C: NativeClass,
C::UserData: Map,
T: ToVariant,
F: Fn(&C) -> T,
{
unsafe {
let rust_ty = C::UserData::clone_from_user_data_unchecked(class as *const _);
let func = &mut *(method as *mut F);
match rust_ty.map(|rust_ty| func(rust_ty)) {
Ok(ret) => ret.to_variant().forget(),
Err(err) => {
godot_error!("gdnative-core: cannot call property getter: {:?}", err);
Variant::new().to_sys()
}
}
}
}
get.get_func = Some(invoke::<C, F, T>);
extern "C" fn free_func<F>(data: *mut libc::c_void) {
unsafe {
drop(Box::from_raw(data as *mut F));
}
}
get.free_func = Some(free_func::<F>);
get
}
}