BGE API cleanup: introduction of a generic framework to link Python attributes to logic brick class member. See KX_PYATTRIBUTE macros in PyObjectPlus.h.

This commit is contained in:
Benoit Bolsee 2008-12-31 20:35:20 +00:00
parent eee013d9b9
commit 2dfd34994f
7 changed files with 638 additions and 217 deletions

@ -131,6 +131,449 @@ int PyObjectPlus::_setattr(const STR_String& attr, PyObject *value)
return 1; return 1;
} }
PyObject *PyObjectPlus::_getattr_self(const PyAttributeDef attrlist[], void *self, const STR_String &attr)
{
const PyAttributeDef *attrdef;
for (attrdef=attrlist; attrdef->m_name != NULL; attrdef++)
{
if (attr == attrdef->m_name)
{
if (attrdef->m_type == KX_PYATTRIBUTE_TYPE_DUMMY)
{
// fake attribute, ignore
return NULL;
}
char *ptr = reinterpret_cast<char*>(self)+attrdef->m_offset;
if (attrdef->m_length > 1)
{
PyObject* resultlist = PyList_New(attrdef->m_length);
for (int i=0; i<attrdef->m_length; i++)
{
switch (attrdef->m_type) {
case KX_PYATTRIBUTE_TYPE_BOOL:
{
bool *val = reinterpret_cast<bool*>(ptr);
ptr += sizeof(bool);
PyList_SetItem(resultlist,i,PyInt_FromLong(*val));
break;
}
case KX_PYATTRIBUTE_TYPE_SHORT:
{
short int *val = reinterpret_cast<short int*>(ptr);
ptr += sizeof(short int);
PyList_SetItem(resultlist,i,PyInt_FromLong(*val));
break;
}
case KX_PYATTRIBUTE_TYPE_INT:
{
int *val = reinterpret_cast<int*>(ptr);
ptr += sizeof(int);
PyList_SetItem(resultlist,i,PyInt_FromLong(*val));
break;
}
case KX_PYATTRIBUTE_TYPE_FLOAT:
{
float *val = reinterpret_cast<float*>(ptr);
ptr += sizeof(float);
PyList_SetItem(resultlist,i,PyFloat_FromDouble(*val));
break;
}
default:
// no support for array of complex data
return NULL;
}
}
return resultlist;
}
else
{
switch (attrdef->m_type) {
case KX_PYATTRIBUTE_TYPE_BOOL:
{
bool *val = reinterpret_cast<bool*>(ptr);
return PyInt_FromLong(*val);
}
case KX_PYATTRIBUTE_TYPE_SHORT:
{
short int *val = reinterpret_cast<short int*>(ptr);
return PyInt_FromLong(*val);
}
case KX_PYATTRIBUTE_TYPE_INT:
{
int *val = reinterpret_cast<int*>(ptr);
return PyInt_FromLong(*val);
}
case KX_PYATTRIBUTE_TYPE_FLOAT:
{
float *val = reinterpret_cast<float*>(ptr);
return PyFloat_FromDouble(*val);
}
case KX_PYATTRIBUTE_TYPE_STRING:
{
STR_String *val = reinterpret_cast<STR_String*>(ptr);
return PyString_FromString(*val);
}
default:
return NULL;
}
}
}
}
return NULL;
}
int PyObjectPlus::_setattr_self(const PyAttributeDef attrlist[], void *self, const STR_String &attr, PyObject *value)
{
const PyAttributeDef *attrdef;
void *undoBuffer = NULL;
void *sourceBuffer = NULL;
size_t bufferSize = 0;
for (attrdef=attrlist; attrdef->m_name != NULL; attrdef++)
{
if (attr == attrdef->m_name)
{
if (attrdef->m_access == KX_PYATTRIBUTE_RO ||
attrdef->m_type == KX_PYATTRIBUTE_TYPE_DUMMY)
{
PyErr_SetString(PyExc_AttributeError, "property is read-only");
return 1;
}
char *ptr = reinterpret_cast<char*>(self)+attrdef->m_offset;
if (attrdef->m_length > 1)
{
if (!PySequence_Check(value))
{
PyErr_SetString(PyExc_TypeError, "expected a sequence");
return 1;
}
if (PySequence_Size(value) != attrdef->m_length)
{
PyErr_SetString(PyExc_TypeError, "incorrect number of elements in sequence");
return 1;
}
switch (attrdef->m_type)
{
case KX_PYATTRIBUTE_TYPE_BOOL:
bufferSize = sizeof(bool);
break;
case KX_PYATTRIBUTE_TYPE_SHORT:
bufferSize = sizeof(short int);
break;
case KX_PYATTRIBUTE_TYPE_INT:
bufferSize = sizeof(int);
break;
case KX_PYATTRIBUTE_TYPE_FLOAT:
bufferSize = sizeof(float);
break;
default:
// should not happen
PyErr_SetString(PyExc_AttributeError, "Unsupported attribute type, report to blender.org");
return 1;
}
// let's implement a smart undo method
bufferSize *= attrdef->m_length;
undoBuffer = malloc(bufferSize);
sourceBuffer = ptr;
if (undoBuffer)
{
memcpy(undoBuffer, sourceBuffer, bufferSize);
}
for (int i=0; i<attrdef->m_length; i++)
{
PyObject *item = PySequence_GetItem(value, i); /* new ref */
// we can decrement the reference immediately, the reference count
// is at least 1 because the item is part of an array
Py_DECREF(item);
switch (attrdef->m_type)
{
case KX_PYATTRIBUTE_TYPE_BOOL:
{
bool *var = reinterpret_cast<bool*>(ptr);
ptr += sizeof(bool);
if (PyInt_Check(item))
{
*var = (PyInt_AsLong(item) != 0);
}
else if (PyBool_Check(item))
{
*var = (item == Py_True);
}
else
{
PyErr_SetString(PyExc_TypeError, "expected an integer or a bool");
goto UNDO_AND_ERROR;
}
break;
}
case KX_PYATTRIBUTE_TYPE_SHORT:
{
short int *var = reinterpret_cast<short int*>(ptr);
ptr += sizeof(short int);
if (PyInt_Check(item))
{
long val = PyInt_AsLong(item);
if (val < attrdef->m_imin || val > attrdef->m_imax)
{
PyErr_SetString(PyExc_ValueError, "item value out of range");
goto UNDO_AND_ERROR;
}
*var = (short int)val;
}
else
{
PyErr_SetString(PyExc_TypeError, "expected an integer");
goto UNDO_AND_ERROR;
}
break;
}
case KX_PYATTRIBUTE_TYPE_INT:
{
int *var = reinterpret_cast<int*>(ptr);
ptr += sizeof(int);
if (PyInt_Check(item))
{
long val = PyInt_AsLong(item);
if (val < attrdef->m_imin || val > attrdef->m_imax)
{
PyErr_SetString(PyExc_ValueError, "item value out of range");
goto UNDO_AND_ERROR;
}
*var = (int)val;
}
else
{
PyErr_SetString(PyExc_TypeError, "expected an integer");
goto UNDO_AND_ERROR;
}
break;
}
case KX_PYATTRIBUTE_TYPE_FLOAT:
{
float *var = reinterpret_cast<float*>(ptr);
ptr += sizeof(float);
if (PyFloat_Check(item))
{
double val = PyFloat_AsDouble(item);
if (val < attrdef->m_fmin || val > attrdef->m_fmax)
{
PyErr_SetString(PyExc_ValueError, "item value out of range");
goto UNDO_AND_ERROR;
}
*var = (float)val;
}
else
{
PyErr_SetString(PyExc_TypeError, "expected a float");
goto UNDO_AND_ERROR;
}
break;
}
default:
// should not happen
PyErr_SetString(PyExc_AttributeError, "attribute type check error, report to blender.org");
goto UNDO_AND_ERROR;
}
}
// no error, call check function if any
if (attrdef->m_function != NULL)
{
if ((*attrdef->m_function)(self) != 0)
{
// post check returned an error, restore values
UNDO_AND_ERROR:
if (undoBuffer)
{
memcpy(sourceBuffer, undoBuffer, bufferSize);
free(undoBuffer);
}
return 1;
}
}
if (undoBuffer)
free(undoBuffer);
return 0;
}
else // simple attribute value
{
if (attrdef->m_function != NULL)
{
// post check function is provided, prepare undo buffer
sourceBuffer = ptr;
switch (attrdef->m_type)
{
case KX_PYATTRIBUTE_TYPE_BOOL:
bufferSize = sizeof(bool);
break;
case KX_PYATTRIBUTE_TYPE_SHORT:
bufferSize = sizeof(short);
break;
case KX_PYATTRIBUTE_TYPE_INT:
bufferSize = sizeof(int);
break;
case KX_PYATTRIBUTE_TYPE_FLOAT:
bufferSize = sizeof(float);
break;
case KX_PYATTRIBUTE_TYPE_STRING:
sourceBuffer = reinterpret_cast<STR_String*>(ptr)->Ptr();
if (sourceBuffer)
bufferSize = strlen(reinterpret_cast<char*>(sourceBuffer))+1;
break;
default:
PyErr_SetString(PyExc_AttributeError, "unknown attribute type, report to blender.org");
return 1;
}
if (bufferSize)
{
undoBuffer = malloc(bufferSize);
if (undoBuffer)
{
memcpy(undoBuffer, sourceBuffer, bufferSize);
}
}
}
switch (attrdef->m_type)
{
case KX_PYATTRIBUTE_TYPE_BOOL:
{
bool *var = reinterpret_cast<bool*>(ptr);
if (PyInt_Check(value))
{
*var = (PyInt_AsLong(value) != 0);
}
else if (PyBool_Check(value))
{
*var = (value == Py_True);
}
else
{
PyErr_SetString(PyExc_TypeError, "expected an integer or a bool");
goto FREE_AND_ERROR;
}
break;
}
case KX_PYATTRIBUTE_TYPE_SHORT:
{
short int *var = reinterpret_cast<short int*>(ptr);
if (PyInt_Check(value))
{
long val = PyInt_AsLong(value);
if (val < attrdef->m_imin || val > attrdef->m_imax)
{
PyErr_SetString(PyExc_ValueError, "value out of range");
goto FREE_AND_ERROR;
}
*var = (short int)val;
}
else
{
PyErr_SetString(PyExc_TypeError, "expected an integer");
goto FREE_AND_ERROR;
}
break;
}
case KX_PYATTRIBUTE_TYPE_INT:
{
int *var = reinterpret_cast<int*>(ptr);
if (PyInt_Check(value))
{
long val = PyInt_AsLong(value);
if (val < attrdef->m_imin || val > attrdef->m_imax)
{
PyErr_SetString(PyExc_ValueError, "value out of range");
goto FREE_AND_ERROR;
}
*var = (int)val;
}
else
{
PyErr_SetString(PyExc_TypeError, "expected an integer");
goto FREE_AND_ERROR;
}
break;
}
case KX_PYATTRIBUTE_TYPE_FLOAT:
{
float *var = reinterpret_cast<float*>(ptr);
if (PyFloat_Check(value))
{
double val = PyFloat_AsDouble(value);
if (val < attrdef->m_fmin || val > attrdef->m_fmax)
{
PyErr_SetString(PyExc_ValueError, "value out of range");
goto FREE_AND_ERROR;
}
*var = (float)val;
}
else
{
PyErr_SetString(PyExc_TypeError, "expected a float");
goto FREE_AND_ERROR;
}
break;
}
case KX_PYATTRIBUTE_TYPE_STRING:
{
STR_String *var = reinterpret_cast<STR_String*>(ptr);
if (PyString_Check(value))
{
char *val = PyString_AsString(value);
if (strlen(val) < attrdef->m_imin || strlen(val) > attrdef->m_imax)
{
PyErr_SetString(PyExc_ValueError, "string length out of range");
goto FREE_AND_ERROR;
}
*var = val;
}
else
{
PyErr_SetString(PyExc_TypeError, "expected a string");
goto FREE_AND_ERROR;
}
break;
}
default:
// should not happen
PyErr_SetString(PyExc_AttributeError, "unknown attribute type, report to blender.org");
goto FREE_AND_ERROR;
}
}
// check if post processing is needed
if (attrdef->m_function != NULL)
{
if ((*attrdef->m_function)(self) != 0)
{
// restore value
if (undoBuffer)
{
if (attrdef->m_type == KX_PYATTRIBUTE_TYPE_STRING)
{
// special case for STR_String: restore the string
STR_String *var = reinterpret_cast<STR_String*>(ptr);
*var = reinterpret_cast<char*>(undoBuffer);
}
else
{
// other field type have direct values
memcpy(ptr, undoBuffer, bufferSize);
}
}
FREE_AND_ERROR:
if (undoBuffer)
free(undoBuffer);
return 1;
}
}
if (undoBuffer)
free(undoBuffer);
return 0;
}
}
return -1;
}
/*------------------------------ /*------------------------------
* PyObjectPlus repr -- representations * PyObjectPlus repr -- representations
------------------------------*/ ------------------------------*/

@ -86,6 +86,7 @@ static inline void Py_Fatal(const char *M) {
public: \ public: \
static PyTypeObject Type; \ static PyTypeObject Type; \
static PyMethodDef Methods[]; \ static PyMethodDef Methods[]; \
static PyAttributeDef Attributes[]; \
static PyParentObject Parents[]; \ static PyParentObject Parents[]; \
virtual PyTypeObject *GetType(void) {return &Type;}; \ virtual PyTypeObject *GetType(void) {return &Type;}; \
virtual PyParentObject *GetParents(void) {return Parents;} virtual PyParentObject *GetParents(void) {return Parents;}
@ -210,6 +211,104 @@ PyObject* class_name::Py##method_name(PyObject*, PyObject* value)
const char class_name::method_name##_doc[] = doc_string; \ const char class_name::method_name##_doc[] = doc_string; \
PyObject* class_name::Py##method_name(PyObject*) PyObject* class_name::Py##method_name(PyObject*)
/**
* Attribute management
*/
enum KX_PYATTRIBUTE_TYPE {
KX_PYATTRIBUTE_TYPE_BOOL,
KX_PYATTRIBUTE_TYPE_SHORT,
KX_PYATTRIBUTE_TYPE_INT,
KX_PYATTRIBUTE_TYPE_FLOAT,
KX_PYATTRIBUTE_TYPE_STRING,
KX_PYATTRIBUTE_TYPE_DUMMY,
};
enum KX_PYATTRIBUTE_ACCESS {
KX_PYATTRIBUTE_RW,
KX_PYATTRIBUTE_RO
};
typedef int (*KX_PYATTRIBUTE_FUNCTION)(void *self);
typedef struct KX_PYATTRIBUTE_DEF {
const char *m_name; // name of the python attribute
KX_PYATTRIBUTE_TYPE m_type; // type of value
KX_PYATTRIBUTE_ACCESS m_access; // read/write access or read-only
int m_imin; // minimum value in case of integer attributes
int m_imax; // maximum value in case of integer attributes
float m_fmin; // minimum value in case of float attributes
float m_fmax; // maximum value in case of float attributes
size_t m_offset; // position of field in structure
size_t m_length; // length of array, 1=simple attribute
KX_PYATTRIBUTE_FUNCTION m_function; // static function to check the assignment, returns 0 if no error
// The following pointers are just used to have compile time check for attribute type.
// It would have been good to use a union but that would require C99 compatibility
// to initialize specific union fields through designated initializers.
struct {
bool *m_boolPtr;
short int *m_shortPtr;
int *m_intPtr;
float *m_floatPtr;
STR_String *m_stringPtr;
} m_typeCheck;
} PyAttributeDef;
#define KX_PYATTRIBUTE_DUMMY(name) \
{ name, KX_PYATTRIBUTE_TYPE_DUMMY, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, 0, 1, NULL, {NULL, NULL, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_BOOL_RW(name,object,field) \
{ name, KX_PYATTRIBUTE_TYPE_BOOL, KX_PYATTRIBUTE_RW, 0, 1, 0.f, 0.f, offsetof(object,field), 1, NULL, {&((object *)0)->field, NULL, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_BOOL_RW_CHECK(name,object,field,function) \
{ name, KX_PYATTRIBUTE_TYPE_BOOL, KX_PYATTRIBUTE_RW, 0, 1, 0.f, 0.f, offsetof(object,field), 1, &object::function, {&((object *)0)->field, NULL, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_BOOL_RO(name,object,field) \
{ name, KX_PYATTRIBUTE_TYPE_BOOL, KX_PYATTRIBUTE_RO, 0, 1, 0.f, 0.f, offsetof(object,field), 1, NULL, {&((object *)0)->field, NULL, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_SHORT_RW(name,min,max,object,field) \
{ name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, offsetof(object,field), 1, NULL, {NULL, &((object *)0)->field, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_SHORT_RW_CHECK(name,min,max,object,field,function) \
{ name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, offsetof(object,field), 1, &object::function, {NULL, &((object *)0)->field, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_SHORT_RO(name,object,field) \
{ name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, offsetof(object,field), 1, NULL, {NULL, &((object *)0)->field, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_SHORT_ARRAY_RW(name,min,max,object,field,length) \
{ name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, offsetof(object,field), length, NULL, {NULL, &((object *)0)->field, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_SHORT_ARRAY_RW_CHECK(name,min,max,object,field,length,function) \
{ name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, offsetof(object,field), length, &object::function, {NULL, &((object *)0)->field, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_SHORT_ARRAY_RO(name,object,field,length) \
{ name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, offsetof(object,field), length, NULL, {NULL, &((object *)0)->field, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_INT_RW(name,min,max,object,field) \
{ name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, offsetof(object,field), 1, NULL, {NULL, NULL, &((object *)0)->field, NULL, NULL} }
#define KX_PYATTRIBUTE_INT_RW_CHECK(name,min,max,object,field,function) \
{ name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, offsetof(object,field), 1, &object::function, {NULL, NULL, &((object *)0)->field, NULL, NULL} }
#define KX_PYATTRIBUTE_INT_RO(name,object,field) \
{ name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, offsetof(object,field), 1, NULL, {NULL, NULL, &((object *)0)->field, NULL, NULL} }
#define KX_PYATTRIBUTE_INT_ARRAY_RW(name,min,max,object,field,length) \
{ name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, offsetof(object,field), length, NULL, {NULL, NULL, &((object *)0)->field, NULL, NULL} }
#define KX_PYATTRIBUTE_INT_ARRAY_RW_CHECK(name,min,max,object,field,length,function) \
{ name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, offsetof(object,field), length, &object::function, {NULL, NULL, &((object *)0)->field, NULL, NULL} }
#define KX_PYATTRIBUTE_INT_ARRAY_RO(name,object,field,length) \
{ name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, offsetof(object,field), length, NULL, {NULL, NULL, &((object *)0)->field, NULL, NULL} }
#define KX_PYATTRIBUTE_FLOAT_RW(name,min,max,object,field) \
{ name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RW, 0, 0, min, max, offsetof(object,field), 1, NULL, {NULL, NULL, NULL, &((object *)0)->field, NULL} }
#define KX_PYATTRIBUTE_FLOAT_RW_CHECK(name,min,max,object,field,function) \
{ name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RW, 0, 0, min, max, offsetof(object,field), 1, &object::function, {NULL, NULL, NULL, &((object *)0)->field, NULL} }
#define KX_PYATTRIBUTE_FLOAT_RO(name,object,field) \
{ name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, offsetof(object,field), 1, NULL, {NULL, NULL, NULL, &((object *)0)->field, NULL} }
#define KX_PYATTRIBUTE_FLOAT_ARRAY_RW(name,min,max,object,field,length) \
{ name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RW, 0, 0, min, max, offsetof(object,field), length, NULL, {NULL, NULL, NULL, &((object *)0)->field, NULL} }
#define KX_PYATTRIBUTE_FLOAT_ARRAY_RW_CHECK(name,min,max,object,field,length,function) \
{ name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RW, 0, 0, min, max, offsetof(object,field), length, &object::function, {NULL, NULL, NULL, &((object *)0)->field, NULL} }
#define KX_PYATTRIBUTE_FLOAT_ARRAY_RO(name,object,field,length) \
{ name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, offsetof(object,field), length, NULL, {NULL, NULL, NULL, &((object *)0)->field, NULL} }
#define KX_PYATTRIBUTE_STRING_RW(name,min,max,object,field) \
{ name, KX_PYATTRIBUTE_TYPE_STRING, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, offsetof(object,field), 1, NULL, {NULL, NULL, NULL, NULL, &((object *)0)->field} }
#define KX_PYATTRIBUTE_STRING_RW_CHECK(name,min,max,object,field,function) \
{ name, KX_PYATTRIBUTE_TYPE_STRING, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, offsetof(object,field), 1, &object::function, {NULL, NULL, NULL, NULL, &((object *)0)->field} }
#define KX_PYATTRIBUTE_STRING_RO(name,object,field) \
{ name, KX_PYATTRIBUTE_TYPE_STRING, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, offsetof(object,field), 1 , NULL, {NULL, NULL, NULL, NULL, &((object *)0)->field} }
/*------------------------------ /*------------------------------
* PyObjectPlus * PyObjectPlus
------------------------------*/ ------------------------------*/
@ -240,6 +339,8 @@ public:
{ {
return ((PyObjectPlus*) PyObj)->_getattr(STR_String(attr)); return ((PyObjectPlus*) PyObj)->_getattr(STR_String(attr));
} }
static PyObject *_getattr_self(const PyAttributeDef attrlist[], void *self, const STR_String &attr);
static int _setattr_self(const PyAttributeDef attrlist[], void *self, const STR_String &attr, PyObject *value);
virtual int _delattr(const STR_String& attr); virtual int _delattr(const STR_String& attr);
virtual int _setattr(const STR_String& attr, PyObject *value); // _setattr method virtual int _setattr(const STR_String& attr, PyObject *value); // _setattr method

@ -327,6 +327,21 @@ PyMethodDef SCA_JoystickSensor::Methods[] = {
{NULL,NULL} //Sentinel {NULL,NULL} //Sentinel
}; };
PyAttributeDef SCA_JoystickSensor::Attributes[] = {
KX_PYATTRIBUTE_SHORT_RW("index",0,JOYINDEX_MAX-1,SCA_JoystickSensor,m_joyindex),
KX_PYATTRIBUTE_INT_RW("threshold",0,32768,SCA_JoystickSensor,m_precision),
KX_PYATTRIBUTE_INT_RW("button",0,100,SCA_JoystickSensor,m_button),
KX_PYATTRIBUTE_INT_ARRAY_RW_CHECK("axis",0,3,SCA_JoystickSensor,m_axis,2,CheckAxis),
KX_PYATTRIBUTE_INT_ARRAY_RW_CHECK("hat",0,12,SCA_JoystickSensor,m_hat,2,CheckHat),
// dummy attributes will just be read-only in _setattr
// you still need to defined them in _getattr
KX_PYATTRIBUTE_DUMMY("axisPosition"),
KX_PYATTRIBUTE_DUMMY("numAxis"),
KX_PYATTRIBUTE_DUMMY("numButtons"),
KX_PYATTRIBUTE_DUMMY("numHats"),
KX_PYATTRIBUTE_DUMMY("connected"),
{ NULL } //Sentinel
};
PyObject* SCA_JoystickSensor::_getattr(const STR_String& attr) { PyObject* SCA_JoystickSensor::_getattr(const STR_String& attr) {
SCA_Joystick *joy = m_pJoystickMgr->GetJoystickDevice(m_joyindex); SCA_Joystick *joy = m_pJoystickMgr->GetJoystickDevice(m_joyindex);
@ -348,100 +363,17 @@ PyObject* SCA_JoystickSensor::_getattr(const STR_String& attr) {
if (attr == "connected") { if (attr == "connected") {
return PyBool_FromLong( joy ? joy->Connected() : 0 ); return PyBool_FromLong( joy ? joy->Connected() : 0 );
} }
if (attr == "index") { PyObject* object = _getattr_self(Attributes, this, attr);
return PyInt_FromLong(m_joyindex); if (object != NULL)
} return object;
if (attr == "threshold") {
return PyInt_FromLong(m_precision);
}
if (attr == "button") {
return PyInt_FromLong(m_button);
}
if (attr == "axis") {
return Py_BuildValue("[ii]",m_axis, m_axisf);
}
if (attr == "hat") {
return Py_BuildValue("[ii]",m_hat, m_hatf);
}
_getattr_up(SCA_ISensor); _getattr_up(SCA_ISensor);
} }
int SCA_JoystickSensor::_setattr(const STR_String& attr, PyObject *value) { int SCA_JoystickSensor::_setattr(const STR_String& attr, PyObject *value)
if (attr == "axisPosition") { {
PyErr_SetString(PyExc_AttributeError, "axisPosition is read only"); int ret = _setattr_self(Attributes, this, attr, value);
return 1; if (ret >= 0)
} return ret;
if (attr == "numAxis") {
PyErr_SetString(PyExc_AttributeError, "numaxis is read only");
return 1;
}
if (attr == "numButtons") {
PyErr_SetString(PyExc_AttributeError, "numbuttons is read only");
return 1;
}
if (attr == "numHats") {
PyErr_SetString(PyExc_AttributeError, "numhats is read only");
return 1;
}
if (attr == "connected") {
PyErr_SetString(PyExc_AttributeError, "connected is read only");
return 1;
}
if (PyInt_Check(value)) {
int ival = PyInt_AsLong(value);
if (attr == "index") {
if (ival < 0 || ival >= JOYINDEX_MAX) {
PyErr_SetString(PyExc_ValueError, "joystick index out of range");
return 1;
}
m_joyindex = ival;
} else if (attr == "threshold") {
m_precision = ival;
} else if (attr == "button") {
if (ival < 0) {
PyErr_SetString(PyExc_ValueError, "button out of range");
return 1;
}
m_button = ival;
}
return 0;
}
if (PySequence_Check(value)) {
if (attr == "axis" || attr == "hat") {
if (PySequence_Size(value) != 2) {
PyErr_SetString(PyExc_ValueError, "value must be sequence of 2 integers");
return 1;
}
int ival = -1;
int ivalf = -1;
PyObject *item = PySequence_GetItem(value, 0); /* new ref */
ival = PyInt_AsLong(item);
Py_DECREF(item);
item = PySequence_GetItem(value, 1); /* new ref */
ivalf = PyInt_AsLong(item);
Py_DECREF(item);
if (PyErr_Occurred()) {
PyErr_SetString(PyExc_ValueError, "one or more of the items in the sequence was not an integer");
return 1;
}
if (attr == "axis") {
if (ival < 1 || ival > 2 || ivalf < 0 || ivalf > 3) {
PyErr_SetString(PyExc_ValueError, "items in the sequence are out of range");
return 1;
}
m_axis = ival;
m_axisf = ivalf;
} else {
if (ival < 1 || ival > 2) {
PyErr_SetString(PyExc_ValueError, "items in the sequence are out of range");
return 1;
}
m_hat = ival;
m_hatf = ivalf;
}
}
return 0;
}
return SCA_ISensor::_setattr(attr, value); return SCA_ISensor::_setattr(attr, value);
} }

@ -37,11 +37,11 @@ class SCA_JoystickSensor :public SCA_ISensor
class SCA_JoystickManager* m_pJoystickMgr; class SCA_JoystickManager* m_pJoystickMgr;
/** /**
* Axis 1-or-2 * Axis 1-or-2, MUST be followed by m_axisf
*/ */
int m_axis; int m_axis;
/** /**
* Axis flag to find direction * Axis flag to find direction, MUST be an int
*/ */
int m_axisf; int m_axisf;
/** /**
@ -53,11 +53,11 @@ class SCA_JoystickSensor :public SCA_ISensor
*/ */
int m_buttonf; int m_buttonf;
/** /**
* The actual hat * The actual hat. MUST be followed by m_hatf
*/ */
int m_hat; int m_hat;
/** /**
* Flag to find direction 1-12 * Flag to find direction 0-11, MUST be an int
*/ */
int m_hatf; int m_hatf;
/** /**
@ -148,6 +148,28 @@ public:
KX_PYMETHOD_DOC_NOARGS(SCA_JoystickSensor,NumberOfHats); KX_PYMETHOD_DOC_NOARGS(SCA_JoystickSensor,NumberOfHats);
KX_PYMETHOD_DOC_NOARGS(SCA_JoystickSensor,Connected); KX_PYMETHOD_DOC_NOARGS(SCA_JoystickSensor,Connected);
/* attribute check */
static int CheckAxis(void *self)
{
SCA_JoystickSensor* sensor = reinterpret_cast<SCA_JoystickSensor*>(self);
if (sensor->m_axis < 1 || sensor->m_axis > 2)
{
PyErr_SetString(PyExc_ValueError, "axis number must be 1 or 2");
return 1;
}
return 0;
}
static int CheckHat(void *self)
{
SCA_JoystickSensor* sensor = reinterpret_cast<SCA_JoystickSensor*>(self);
if (sensor->m_hat < 1 || sensor->m_hat > 2)
{
PyErr_SetString(PyExc_ValueError, "hat number must be 1 or 2");
return 1;
}
return 0;
}
}; };
#endif #endif

@ -820,91 +820,29 @@ PyMethodDef SCA_KeyboardSensor::Methods[] = {
{NULL,NULL} //Sentinel {NULL,NULL} //Sentinel
}; };
PyAttributeDef SCA_KeyboardSensor::Attributes[] = {
KX_PYATTRIBUTE_BOOL_RW("useAllKeys",SCA_KeyboardSensor,m_bAllKeys),
KX_PYATTRIBUTE_INT_RW("key",0,1000,SCA_KeyboardSensor,m_hotkey),
KX_PYATTRIBUTE_SHORT_RW("hold1",0,1000,SCA_KeyboardSensor,m_qual),
KX_PYATTRIBUTE_SHORT_RW("hold2",0,1000,SCA_KeyboardSensor,m_qual2),
KX_PYATTRIBUTE_STRING_RW("toggleProperty",0,100,SCA_KeyboardSensor,m_toggleprop),
KX_PYATTRIBUTE_STRING_RW("targetProperty",0,100,SCA_KeyboardSensor,m_targetprop),
{ NULL } //Sentinel
};
PyObject* PyObject*
SCA_KeyboardSensor::_getattr(const STR_String& attr) SCA_KeyboardSensor::_getattr(const STR_String& attr)
{ {
if (attr == "key") PyObject* object = _getattr_self(Attributes, this, attr);
return PyInt_FromLong(m_hotkey); if (object != NULL)
return object;
if (attr == "hold1")
return PyInt_FromLong(m_qual);
if (attr == "hold2")
return PyInt_FromLong(m_qual2);
if (attr == "toggleProperty")
return PyString_FromString(m_toggleprop);
if (attr == "targetProperty")
return PyString_FromString(m_targetprop);
if (attr == "useAllKeys")
return PyInt_FromLong(m_bAllKeys);
_getattr_up(SCA_ISensor); _getattr_up(SCA_ISensor);
} }
int SCA_KeyboardSensor::_setattr(const STR_String& attr, PyObject *value) int SCA_KeyboardSensor::_setattr(const STR_String& attr, PyObject *value)
{ {
if (attr == "key") int ret = _setattr_self(Attributes, this, attr, value);
{ if (ret >= 0)
if (!PyInt_Check(value)){ return ret;
PyErr_SetString(PyExc_TypeError, "expected an integer");
return 1;
}
m_hotkey = PyInt_AsLong(value);
return 0;
}
if (attr == "hold1")
{
if (!PyInt_Check(value)){
PyErr_SetString(PyExc_TypeError, "expected an integer");
return 1;
}
m_qual = PyInt_AsLong(value);
return 0;
}
if (attr == "hold2")
{
if (!PyInt_Check(value)){
PyErr_SetString(PyExc_TypeError, "expected an integer");
return 1;
}
m_qual2 = PyInt_AsLong(value);
return 0;
}
if (attr == "useAllKeys")
{
if (!PyInt_Check(value)){
PyErr_SetString(PyExc_TypeError, "expected an integer");
return 1;
}
m_bAllKeys = (PyInt_AsLong(value) != 0);
return 0;
}
if (attr == "logToggleProperty")
{
if (!PyString_Check(value)){
PyErr_SetString(PyExc_TypeError, "expected a string");
return 1;
}
m_toggleprop = PyString_AsString(value);
return 0;
}
if (attr == "logTargetProperty")
{
if (!PyString_Check(value)){
PyErr_SetString(PyExc_TypeError, "expected a string");
return 1;
}
m_targetprop = PyString_AsString(value);
return 0;
}
return SCA_ISensor::_setattr(attr, value); return SCA_ISensor::_setattr(attr, value);
} }

@ -59,7 +59,7 @@ SCA_MouseSensor::SCA_MouseSensor(SCA_MouseManager* eventmgr,
m_mousemode = mousemode; m_mousemode = mousemode;
m_triggermode = true; m_triggermode = true;
UpdateHotkey(); UpdateHotkey(this);
Init(); Init();
} }
@ -74,33 +74,37 @@ SCA_MouseSensor::~SCA_MouseSensor()
/* Nothing to be done here. */ /* Nothing to be done here. */
} }
void SCA_MouseSensor::UpdateHotkey() int SCA_MouseSensor::UpdateHotkey(void *self)
{ {
// gosh, this function is so damn stupid // gosh, this function is so damn stupid
// its here because of a design mistake in the mouse sensor, it should only // its here because of a design mistake in the mouse sensor, it should only
// have 3 trigger modes (button, wheel, move), and let the user set the // have 3 trigger modes (button, wheel, move), and let the user set the
// hotkey separately, like the other sensors. but instead it has a mode for // hotkey separately, like the other sensors. but instead it has a mode for
// each friggin key and i have to update the hotkey based on it... genius! // each friggin key and i have to update the hotkey based on it... genius!
SCA_MouseSensor* sensor = reinterpret_cast<SCA_MouseSensor*>(self);
switch (m_mousemode) { switch (sensor->m_mousemode) {
case KX_MOUSESENSORMODE_LEFTBUTTON: case KX_MOUSESENSORMODE_LEFTBUTTON:
m_hotkey = SCA_IInputDevice::KX_LEFTMOUSE; sensor->m_hotkey = SCA_IInputDevice::KX_LEFTMOUSE;
break; break;
case KX_MOUSESENSORMODE_MIDDLEBUTTON: case KX_MOUSESENSORMODE_MIDDLEBUTTON:
m_hotkey = SCA_IInputDevice::KX_MIDDLEMOUSE; sensor->m_hotkey = SCA_IInputDevice::KX_MIDDLEMOUSE;
break; break;
case KX_MOUSESENSORMODE_RIGHTBUTTON: case KX_MOUSESENSORMODE_RIGHTBUTTON:
m_hotkey = SCA_IInputDevice::KX_RIGHTMOUSE; sensor->m_hotkey = SCA_IInputDevice::KX_RIGHTMOUSE;
break; break;
case KX_MOUSESENSORMODE_WHEELUP: case KX_MOUSESENSORMODE_WHEELUP:
m_hotkey = SCA_IInputDevice::KX_WHEELUPMOUSE; sensor->m_hotkey = SCA_IInputDevice::KX_WHEELUPMOUSE;
break; break;
case KX_MOUSESENSORMODE_WHEELDOWN: case KX_MOUSESENSORMODE_WHEELDOWN:
m_hotkey = SCA_IInputDevice::KX_WHEELDOWNMOUSE; sensor->m_hotkey = SCA_IInputDevice::KX_WHEELDOWNMOUSE;
break; break;
default: default:
; /* ignore, no hotkey */ ; /* ignore, no hotkey */
} }
// return value is used in _setattr(),
// 0=attribute checked ok (see Attributes array definition)
return 0;
} }
CValue* SCA_MouseSensor::GetReplica() CValue* SCA_MouseSensor::GetReplica()
@ -331,44 +335,25 @@ PyMethodDef SCA_MouseSensor::Methods[] = {
{NULL,NULL} //Sentinel {NULL,NULL} //Sentinel
}; };
PyObject* SCA_MouseSensor::_getattr(const STR_String& attr) { PyAttributeDef SCA_MouseSensor::Attributes[] = {
if (attr == "position") KX_PYATTRIBUTE_SHORT_RW_CHECK("mode",KX_MOUSESENSORMODE_NODEF,KX_MOUSESENSORMODE_MAX-1,SCA_MouseSensor,m_mousemode,UpdateHotkey),
return Py_BuildValue("[ii]", m_x, m_y); KX_PYATTRIBUTE_SHORT_ARRAY_RO("position",SCA_MouseSensor,m_x,2),
{ NULL } //Sentinel
if (attr == "mode") };
return PyInt_FromLong(m_mousemode);
PyObject* SCA_MouseSensor::_getattr(const STR_String& attr)
{
PyObject* object = _getattr_self(Attributes, this, attr);
if (object != NULL)
return object;
_getattr_up(SCA_ISensor); _getattr_up(SCA_ISensor);
} }
int SCA_MouseSensor::_setattr(const STR_String& attr, PyObject *value) int SCA_MouseSensor::_setattr(const STR_String& attr, PyObject *value)
{ {
if (attr == "mode") int ret = _setattr_self(Attributes, this, attr, value);
{ if (ret >= 0)
if (!PyInt_Check(value)){ return ret;
PyErr_SetString(PyExc_TypeError, "expected an integer");
return 1;
}
int val = PyInt_AsLong(value);
if ((val < KX_MOUSESENSORMODE_NODEF)
|| (val > KX_MOUSESENSORMODE_MAX)){
PyErr_SetString(PyExc_ValueError, "invalid mode specified!");
return 1;
}
m_mousemode = val;
UpdateHotkey();
return 0;
}
if (attr == "position")
{
PyErr_SetString(PyExc_AttributeError, "'position' is a read-only property!");
return 1;
}
return SCA_ISensor::_setattr(attr, value); return SCA_ISensor::_setattr(attr, value);
} }

@ -58,7 +58,7 @@ class SCA_MouseSensor : public SCA_ISensor
SCA_IInputDevice::KX_EnumInputs m_hotkey; SCA_IInputDevice::KX_EnumInputs m_hotkey;
/** /**
* valid x coordinate * valid x coordinate, MUST be followed by y coordinate
*/ */
short m_x; short m_x;
@ -87,7 +87,7 @@ class SCA_MouseSensor : public SCA_ISensor
bool isValid(KX_MOUSESENSORMODE); bool isValid(KX_MOUSESENSORMODE);
void UpdateHotkey(); static int UpdateHotkey(void *self);
SCA_MouseSensor(class SCA_MouseManager* keybdmgr, SCA_MouseSensor(class SCA_MouseManager* keybdmgr,
int startx,int starty, int startx,int starty,