mirror of
https://github.com/Relintai/gdnative_python.git
synced 2024-11-12 10:25:08 +01:00
281 lines
8.7 KiB
Cython
281 lines
8.7 KiB
Cython
{% from 'method.tmpl.pyx' import render_method, get_method_bind_register_name %}
|
|
|
|
|
|
{% macro render_class_gdapi_ptrs_init(cls) %}
|
|
|
|
{% if not cls.singleton %}
|
|
global __{{ cls.name }}_constructor
|
|
__{{ cls.name }}_constructor = gdapi10.pandemonium_get_class_constructor("{{ cls.name }}")
|
|
{% endif %}
|
|
|
|
{% for method in cls.methods %}
|
|
global {{ get_method_bind_register_name(cls, method) }}
|
|
{{ get_method_bind_register_name(cls, method) }} = gdapi10.pandemonium_method_bind_get_method("{{ cls.bind_register_name }}", "{{ method.name }}")
|
|
{% endfor %}
|
|
|
|
{% endmacro %}
|
|
|
|
|
|
{# TODO: Handle signals #}
|
|
{% macro render_class(cls) %}
|
|
|
|
{% if not cls.base_class %}
|
|
from cpython.object cimport PyObject_GenericGetAttr, PyObject_GenericSetAttr
|
|
{% endif %}
|
|
|
|
{% if not cls.singleton %}
|
|
cdef pandemonium_class_constructor __{{ cls.name }}_constructor = NULL
|
|
{% endif %}
|
|
|
|
{% for method in cls.methods %}
|
|
cdef pandemonium_method_bind *{{ get_method_bind_register_name(cls, method) }} = NULL
|
|
{% endfor %}
|
|
|
|
cdef class {{ cls.name }}({{ cls.base_class }}):
|
|
{% if not cls.base_class %}
|
|
# free is virtual but this is not marked in api.json :'(
|
|
def free(self):
|
|
with nogil:
|
|
gdapi10.pandemonium_object_destroy(self._gd_ptr)
|
|
|
|
def __init__(self):
|
|
raise RuntimeError(
|
|
f"Use `new()` method to instantiate non-refcounted Pandemonium object (and don't forget to free it !)"
|
|
)
|
|
|
|
def __repr__(self):
|
|
return f"<{type(self).__name__} wrapper on 0x{<size_t>self._gd_ptr:x}>"
|
|
|
|
@staticmethod
|
|
cdef inline Object cast_from_variant(const pandemonium_variant *p_gdvar):
|
|
cdef pandemonium_object *ptr = gdapi10.pandemonium_variant_as_object(p_gdvar)
|
|
# Retreive class
|
|
cdef GDString classname = GDString.__new__(GDString)
|
|
with nogil:
|
|
gdapi10.pandemonium_method_bind_ptrcall(
|
|
__methbind__Object__get_class,
|
|
ptr,
|
|
NULL,
|
|
&classname._gd_data
|
|
)
|
|
return globals()[str(classname)]._from_ptr(<size_t>ptr)
|
|
|
|
@staticmethod
|
|
cdef inline Object cast_from_ptr(pandemonium_object *ptr):
|
|
# Retreive class
|
|
cdef GDString classname = GDString.__new__(GDString)
|
|
with nogil:
|
|
gdapi10.pandemonium_method_bind_ptrcall(
|
|
__methbind__Object__get_class,
|
|
ptr,
|
|
NULL,
|
|
&classname._gd_data
|
|
)
|
|
return globals()[str(classname)]._from_ptr(<size_t>ptr)
|
|
|
|
def __eq__(self, other):
|
|
try:
|
|
return self._gd_ptr == (<{{ cls.name }}>other)._gd_ptr
|
|
except TypeError:
|
|
return False
|
|
|
|
def __ne__(self, other):
|
|
try:
|
|
return self._gd_ptr != (<{{ cls.name }}>other)._gd_ptr
|
|
except TypeError:
|
|
return True
|
|
|
|
def __getattr__(self, name):
|
|
cdef GDString gdname = GDString(name)
|
|
cdef GDString gdnamefield = GDString("name")
|
|
|
|
# If a script is attached to the object, we expose here it methods
|
|
if not hasattr(type(self), '__exposed_python_class'):
|
|
if self.has_method(name):
|
|
|
|
def _call(*args):
|
|
return {{ cls.name }}.callv(self, gdname, Array(args))
|
|
|
|
return _call
|
|
# from functools import partial
|
|
# return partial(self.call, gdname)
|
|
|
|
elif any(x for x in self.get_property_list() if x[gdnamefield] == gdname):
|
|
# TODO: Pandemonium currently lacks a `has_property` method
|
|
return self.get(gdname)
|
|
|
|
raise AttributeError(
|
|
f"`{type(self).__name__}` object has no attribute `{name}`"
|
|
)
|
|
|
|
def __setattr__(self, name, value):
|
|
cdef GDString gdname = GDString(name)
|
|
cdef GDString gdnamefield = GDString("name")
|
|
|
|
if hasattr(type(self), '__exposed_python_class'):
|
|
PyObject_GenericSetAttr(self, name, value)
|
|
return
|
|
|
|
# Could retrieve the item inside the Pandemonium class, try to look into
|
|
# the attached script if it has one
|
|
else:
|
|
if any(x for x in self.get_property_list() if x[gdnamefield] == gdname):
|
|
# TODO: Pandemonium currently lacks a `has_property` method
|
|
self.set(name, value)
|
|
return
|
|
|
|
raise AttributeError(
|
|
f"`{type(self).__name__}` object has no attribute `{name}`"
|
|
)
|
|
|
|
def call(self, name, *args):
|
|
return self.callv(name, Array(args))
|
|
|
|
{% endif %}
|
|
|
|
{% if not cls.singleton and cls.instantiable %}
|
|
|
|
{% if cls.is_reference %}
|
|
def __init__(self):
|
|
if __{{ cls.name }}_constructor == NULL:
|
|
raise NotImplementedError(__ERR_MSG_BINDING_NOT_AVAILABLE)
|
|
cdef pandemonium_bool __ret
|
|
with nogil:
|
|
self._gd_ptr = __{{ cls["name"] }}_constructor()
|
|
|
|
if self._gd_ptr is NULL:
|
|
raise MemoryError
|
|
|
|
gdapi10.pandemonium_method_bind_ptrcall(
|
|
__methbind__Reference__init_ref,
|
|
self._gd_ptr,
|
|
NULL,
|
|
&__ret
|
|
)
|
|
{% else %}
|
|
@staticmethod
|
|
def new():
|
|
if __{{ cls.name }}_constructor == NULL:
|
|
raise NotImplementedError(__ERR_MSG_BINDING_NOT_AVAILABLE)
|
|
# Call to __new__ bypasses __init__ constructor
|
|
cdef {{ cls.name }} wrapper = {{ cls.name }}.__new__({{ cls.name }})
|
|
with nogil:
|
|
wrapper._gd_ptr = __{{ cls.name }}_constructor()
|
|
if wrapper._gd_ptr is NULL:
|
|
raise MemoryError
|
|
return wrapper
|
|
{% endif %}
|
|
|
|
{% if cls.name == "Reference" %}
|
|
@classmethod
|
|
def new(cls):
|
|
raise RuntimeError(f"Refcounted Pandemonium object must be created with `{ cls.__name__ }()`")
|
|
|
|
def __dealloc__(self):
|
|
cdef pandemonium_bool __ret
|
|
if self._gd_ptr == NULL:
|
|
return
|
|
with nogil:
|
|
gdapi10.pandemonium_method_bind_ptrcall(
|
|
__methbind__Reference__unreference,
|
|
self._gd_ptr,
|
|
NULL,
|
|
&__ret
|
|
)
|
|
if __ret:
|
|
gdapi10.pandemonium_object_destroy(self._gd_ptr)
|
|
{% endif %}
|
|
|
|
{% endif %}
|
|
|
|
@staticmethod
|
|
cdef {{ cls.name }} from_ptr(pandemonium_object *_ptr):
|
|
# Call to __new__ bypasses __init__ constructor
|
|
cdef {{ cls.name }} wrapper = {{ cls.name }}.__new__({{ cls.name }})
|
|
wrapper._gd_ptr = _ptr
|
|
{% if cls.is_reference %}
|
|
# Note we steal the reference from the caller given we
|
|
# don't call `Reference.reference` here
|
|
{% endif %}
|
|
return wrapper
|
|
|
|
{% if not cls.singleton and cls.instantiable %}
|
|
@classmethod
|
|
def _new(cls):
|
|
cdef pandemonium_object* ptr = __{{ cls.name }}_constructor()
|
|
if ptr is NULL:
|
|
raise MemoryError
|
|
return <size_t>ptr
|
|
{% endif %}
|
|
|
|
@staticmethod
|
|
def _from_ptr(ptr):
|
|
# Call to __new__ bypasses __init__ constructor
|
|
cdef {{ cls.name }} wrapper = {{ cls.name }}.__new__({{ cls.name }})
|
|
# /!\ doing `<pandemonium_object*>ptr` would return the address of
|
|
# the PyObject instead of casting it value !
|
|
wrapper._gd_ptr = <pandemonium_object *><size_t>ptr
|
|
{% if cls.is_reference %}
|
|
# Note we steal the reference from the caller given we
|
|
# don't call `Reference.reference` here
|
|
{% endif %}
|
|
return wrapper
|
|
|
|
{% if cls.constants | length %}
|
|
# Constants
|
|
{% endif %}
|
|
{% for key, value in cls.constants.items() %}
|
|
{{ key }} = {{ value }}
|
|
{% endfor %}
|
|
{% if cls.enums | length %}
|
|
# Enums
|
|
{% endif %}
|
|
{% for enum in cls.enums %}
|
|
{{ enum.name }} = IntFlag("{{ enum.name }}", {
|
|
{% for key, value in enum.values.items() %}
|
|
"{{ key }}": {{ value }},
|
|
{% endfor %}
|
|
})
|
|
{% endfor %}
|
|
|
|
{% if cls.methods | length %}
|
|
# Methods
|
|
{% endif %}
|
|
{# TODO: Use typing for params&return #}
|
|
{% for method in cls.methods %}
|
|
{% if method.name != "free" %}
|
|
{{ render_method(cls, method) | indent }}
|
|
{% endif %}
|
|
{% endfor %}
|
|
{% if cls.properties | length %}
|
|
# Properties
|
|
{% endif %}
|
|
{#
|
|
TODO: some properties has / in there name
|
|
TODO: some properties pass a parameter to the setter/getter
|
|
TODO: see PinJoint.params/bias for a good example
|
|
#}
|
|
{% for prop in cls.properties %}
|
|
|
|
@property
|
|
def {{ prop.name }}(self):
|
|
{% if prop.is_supported %}
|
|
return self.{{ prop.getter }}({% if prop.index is not none %}{{ prop.index }}{% endif %})
|
|
{% else %}
|
|
raise NotImplementedError("{{prop.unsupported_reason}}")
|
|
{% endif %}
|
|
|
|
{% if prop.setter %}
|
|
@{{ prop.name }}.setter
|
|
def {{ prop.name }}(self, val):
|
|
{% if prop.is_supported %}
|
|
self.{{ prop.setter }}({% if prop.index is not none %}{{ prop.index }},{% endif %}val)
|
|
{% else %}
|
|
raise NotImplementedError("{{prop.unsupported_reason}}")
|
|
{% endif %}
|
|
{% endif %}
|
|
|
|
{% endfor %}
|
|
|
|
{% endmacro %}
|