1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
use crate::sys;
use crate::ObjectMethodTable;
use libc;
use std::ptr;

/// Trait for Godot API objects. This trait is sealed, and implemented for generated wrapper
/// types.
///
/// # Remarks
///
/// The `cast` method on Godot object types is only for conversion between engine types.
/// To downcast a `NativeScript` type from its base type, see `Instance::try_from_base`.
pub unsafe trait GodotObject: crate::private::godot_object::Sealed {
    fn class_name() -> &'static str;
    #[doc(hidden)]
    unsafe fn to_sys(&self) -> *mut sys::godot_object;
    #[doc(hidden)]
    unsafe fn from_sys(obj: *mut sys::godot_object) -> Self;

    /// Convert from a pointer returned from a ptrcall. For reference-counted types, this takes
    /// the ownership of the returned reference, in Rust parlance. For non-reference-counted
    /// types, its behavior should be exactly the same as `from_sys`. This is needed for
    /// reference-counted types to be properly freed, since any references returned from
    /// ptrcalls are leaked in the process of being cast into a pointer.
    #[doc(hidden)]
    unsafe fn from_return_position_sys(obj: *mut sys::godot_object) -> Self;

    /// Creates a wrapper around the same Godot object that has `'static` lifetime.
    ///
    /// Most Godot APIs expect object arguments with `'static` lifetime. This method may be used
    /// to produce a `'static` wrapper given a reference. For reference-counted types, or classes
    /// that extend `Reference`, this increments the reference count. For manually-managed types,
    /// including all classes that inherit `Node`, this creates an alias.
    ///
    /// # Remarks
    ///
    /// Although manually-managed types are already `unsafe` to use, like raw pointers, this is
    /// `unsafe` because some methods expect `&mut self` receivers. In `0.9.0`, all methods will
    /// take shared references instead, making this safe to call.
    unsafe fn claim(&self) -> Self
    where
        Self: Sized,
    {
        Self::from_sys(self.to_sys())
    }
}

/// GodotObjects that have a zero argument constructor.
pub trait Instanciable: GodotObject {
    fn construct() -> Self;
}

/// Manually managed Godot classes implementing `free`.
pub trait Free {
    unsafe fn godot_free(self);
}

/// Manually managed Godot classes implementing `queue_free`.
pub trait QueueFree {
    unsafe fn godot_queue_free(&mut self);
}

// This function assumes the godot_object is reference counted.
pub unsafe fn add_ref(obj: *mut sys::godot_object) {
    use crate::ReferenceMethodTable;
    let api = crate::private::get_api();
    let addref_method = ReferenceMethodTable::unchecked_get().reference;
    let mut argument_buffer = [ptr::null() as *const libc::c_void; 0];
    let mut ok = false;
    let ok_ptr = &mut ok as *mut bool;
    (api.godot_method_bind_ptrcall)(
        addref_method,
        obj,
        argument_buffer.as_mut_ptr() as *mut _,
        ok_ptr as *mut _,
    );

    // If this assertion blows up it means there is a reference counting bug
    // and we tried to increment the ref count of a dead object (who's ref
    // count is equal to zero).
    debug_assert!(ok);
}

// This function assumes the godot_object is reference counted.
pub unsafe fn unref(obj: *mut sys::godot_object) -> bool {
    use crate::ReferenceMethodTable;
    let unref_method = ReferenceMethodTable::unchecked_get().unreference;
    let mut argument_buffer = [ptr::null() as *const libc::c_void; 0];
    let mut last_reference = false;
    let ret_ptr = &mut last_reference as *mut bool;
    (crate::private::get_api().godot_method_bind_ptrcall)(
        unref_method,
        obj,
        argument_buffer.as_mut_ptr() as *mut _,
        ret_ptr as *mut _,
    );

    last_reference
}

// This function assumes the godot_object is reference counted.
pub unsafe fn init_ref_count(obj: *mut sys::godot_object) {
    use crate::ReferenceMethodTable;
    let init_method = ReferenceMethodTable::unchecked_get().init_ref;
    let mut argument_buffer = [ptr::null() as *const libc::c_void; 0];
    let mut ok = false;
    let ret_ptr = &mut ok as *mut bool;
    (crate::private::get_api().godot_method_bind_ptrcall)(
        init_method,
        obj,
        argument_buffer.as_mut_ptr() as *mut _,
        ret_ptr as *mut _,
    );

    debug_assert!(ok);
}

pub unsafe fn is_class(obj: *mut sys::godot_object, class_name: &str) -> bool {
    let api = crate::private::get_api();
    let method_bind = ObjectMethodTable::get(api).is_class;

    let mut class_name = (api.godot_string_chars_to_utf8_with_len)(
        class_name.as_ptr() as *const _,
        class_name.len() as _,
    );

    let mut argument_buffer = [ptr::null() as *const libc::c_void; 1];
    argument_buffer[0] = (&class_name) as *const _ as *const _;

    let mut ret = false;
    let ret_ptr = &mut ret as *mut _;
    (api.godot_method_bind_ptrcall)(
        method_bind,
        obj,
        argument_buffer.as_mut_ptr() as *mut _,
        ret_ptr as *mut _,
    );

    (api.godot_string_destroy)(&mut class_name);

    ret
}

pub unsafe fn godot_cast<T>(from: *mut sys::godot_object) -> Option<T>
where
    T: GodotObject,
{
    if !is_class(from, T::class_name()) {
        return None;
    }

    Some(T::from_sys(from))
}