From f2f2b6153a2a818ca940a4df5b7dafc743ef2d2f Mon Sep 17 00:00:00 2001 From: Mitchell Stokes Date: Fri, 21 Dec 2012 02:28:59 +0000 Subject: BGE: Adding a Python interface for handling joysticks without needing logic bricks. These new SCA_PythonJoystick objects can be accessed using bge.logic.joysticks, which is a list of joysticks. The length of the list is the number of maximum supported joysticks, and indexes that do not have a joystick available are set to None. This means joysticks can be checked for using something like: if bge.logic.joysticks[0]: activate_player_one() if bge.logic.joysticks[1]: activate_player_two() etc.. The interface exposed by SCA_PythonJoystick is very similar to the joystick logic brick except for one key difference: axis values are normalized to a -1.0 to 1.0 range instead of -32767 to 32767, which is what the logic brick exposed. --- source/gameengine/GameLogic/CMakeLists.txt | 2 + .../gameengine/GameLogic/Joystick/SCA_Joystick.cpp | 9 + .../gameengine/GameLogic/Joystick/SCA_Joystick.h | 5 + .../gameengine/GameLogic/SCA_JoystickManager.cpp | 8 +- source/gameengine/GameLogic/SCA_PythonJoystick.cpp | 184 +++++++++++++++++++++ source/gameengine/GameLogic/SCA_PythonJoystick.h | 56 +++++++ source/gameengine/Ketsji/KX_PythonInit.cpp | 33 ++++ source/gameengine/Ketsji/KX_PythonInitTypes.cpp | 2 + 8 files changed, 296 insertions(+), 3 deletions(-) create mode 100644 source/gameengine/GameLogic/SCA_PythonJoystick.cpp create mode 100644 source/gameengine/GameLogic/SCA_PythonJoystick.h (limited to 'source/gameengine') diff --git a/source/gameengine/GameLogic/CMakeLists.txt b/source/gameengine/GameLogic/CMakeLists.txt index e511704c7f4..ad357bd015b 100644 --- a/source/gameengine/GameLogic/CMakeLists.txt +++ b/source/gameengine/GameLogic/CMakeLists.txt @@ -71,6 +71,7 @@ set(SRC SCA_PropertyEventManager.cpp SCA_PropertySensor.cpp SCA_PythonController.cpp + SCA_PythonJoystick.cpp SCA_PythonKeyboard.cpp SCA_PythonMouse.cpp SCA_RandomActuator.cpp @@ -114,6 +115,7 @@ set(SRC SCA_PropertyEventManager.h SCA_PropertySensor.h SCA_PythonController.h + SCA_PythonJoystick.h SCA_PythonKeyboard.h SCA_PythonMouse.h SCA_RandomActuator.h diff --git a/source/gameengine/GameLogic/Joystick/SCA_Joystick.cpp b/source/gameengine/GameLogic/Joystick/SCA_Joystick.cpp index 8b343be8226..b7fbe958c86 100644 --- a/source/gameengine/GameLogic/Joystick/SCA_Joystick.cpp +++ b/source/gameengine/GameLogic/Joystick/SCA_Joystick.cpp @@ -321,3 +321,12 @@ int SCA_Joystick::pAxisTest(int axisnum) return 0; #endif /* WITH_SDL */ } + +const char *SCA_Joystick::GetName() +{ +#ifdef WITH_SDL + return SDL_JoystickName(m_joyindex); +#else /* WITH_SDL */ + return ""; +#endif /* WITH_SDL */ +} diff --git a/source/gameengine/GameLogic/Joystick/SCA_Joystick.h b/source/gameengine/GameLogic/Joystick/SCA_Joystick.h index 912484a2fe5..dd9fbefa545 100644 --- a/source/gameengine/GameLogic/Joystick/SCA_Joystick.h +++ b/source/gameengine/GameLogic/Joystick/SCA_Joystick.h @@ -192,6 +192,11 @@ public: * Test if the joystick is connected */ int Connected(void); + + /** + * Name of the joytsick + */ + const char *GetName(); }; #endif diff --git a/source/gameengine/GameLogic/SCA_JoystickManager.cpp b/source/gameengine/GameLogic/SCA_JoystickManager.cpp index c21db794e42..780e4e9ce88 100644 --- a/source/gameengine/GameLogic/SCA_JoystickManager.cpp +++ b/source/gameengine/GameLogic/SCA_JoystickManager.cpp @@ -60,14 +60,16 @@ SCA_JoystickManager::~SCA_JoystickManager() void SCA_JoystickManager::NextFrame(double curtime,double deltatime) { + // We should always handle events in case we want to grab them with Python +#ifdef WITH_SDL + SCA_Joystick::HandleEvents(); /* Handle all SDL Joystick events */ +#endif + if (m_sensors.Empty()) { return; } else { ; -#ifdef WITH_SDL - SCA_Joystick::HandleEvents(); /* Handle all SDL Joystick events */ -#endif SG_DList::iterator it(m_sensors); for (it.begin();!it.end();++it) { diff --git a/source/gameengine/GameLogic/SCA_PythonJoystick.cpp b/source/gameengine/GameLogic/SCA_PythonJoystick.cpp new file mode 100644 index 00000000000..ee792111705 --- /dev/null +++ b/source/gameengine/GameLogic/SCA_PythonJoystick.cpp @@ -0,0 +1,184 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Mitchell Stokes. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file gameengine/GameLogic/SCA_PythonJoystick.cpp + * \ingroup gamelogic + */ + + +#include "SCA_PythonJoystick.h" +#include "./Joystick/SCA_Joystick.h" +#include "SCA_IInputDevice.h" + +//#include "GHOST_C-api.h" + +/* ------------------------------------------------------------------------- */ +/* Native functions */ +/* ------------------------------------------------------------------------- */ + +SCA_PythonJoystick::SCA_PythonJoystick(SCA_Joystick* joystick) +: PyObjectPlus(), +m_joystick(joystick) +{ +#ifdef WITH_PYTHON + m_event_dict = PyDict_New(); +#endif +} + +SCA_PythonJoystick::~SCA_PythonJoystick() +{ +#ifdef WITH_PYTHON + PyDict_Clear(m_event_dict); + Py_DECREF(m_event_dict); +#endif +} + +#ifdef WITH_PYTHON + +/* ------------------------------------------------------------------------- */ +/* Python functions */ +/* ------------------------------------------------------------------------- */ +PyObject* SCA_PythonJoystick::py_repr(void) +{ + return PyUnicode_FromString(m_joystick->GetName()); +} + + +/* Integration hooks ------------------------------------------------------- */ +PyTypeObject SCA_PythonJoystick::Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "SCA_PythonJoystick", + sizeof(PyObjectPlus_Proxy), + 0, + py_base_dealloc, + 0, + 0, + 0, + 0, + py_base_repr, + 0,0,0,0,0,0,0,0,0, + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + 0,0,0,0,0,0,0, + Methods, + 0, + 0, + &PyObjectPlus::Type, + 0,0,0,0,0,0, + py_base_new +}; + +PyMethodDef SCA_PythonJoystick::Methods[] = { + {NULL,NULL} //Sentinel +}; + +PyAttributeDef SCA_PythonJoystick::Attributes[] = { + KX_PYATTRIBUTE_RO_FUNCTION("numButtons", SCA_PythonJoystick, pyattr_get_num_x), + KX_PYATTRIBUTE_RO_FUNCTION("numHats", SCA_PythonJoystick, pyattr_get_num_x), + KX_PYATTRIBUTE_RO_FUNCTION("numAxis", SCA_PythonJoystick, pyattr_get_num_x), + KX_PYATTRIBUTE_RO_FUNCTION("activeButtons", SCA_PythonJoystick, pyattr_get_active_buttons), + KX_PYATTRIBUTE_RO_FUNCTION("hatValues", SCA_PythonJoystick, pyattr_get_hat_values), + KX_PYATTRIBUTE_RO_FUNCTION("axisValues", SCA_PythonJoystick, pyattr_get_axis_values), + KX_PYATTRIBUTE_RO_FUNCTION("name", SCA_PythonJoystick, pyattr_get_name), + { NULL } //Sentinel +}; + +// Use one function for numAxis, numButtons, and numHats +PyObject* SCA_PythonJoystick::pyattr_get_num_x(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef) +{ + SCA_PythonJoystick* self = static_cast(self_v); + + if (strcmp(attrdef->m_name, "numButtons") == 0) + return PyLong_FromLong(self->m_joystick->GetNumberOfButtons()); + else if (strcmp(attrdef->m_name, "numAxis") == 0) + return PyLong_FromLong(self->m_joystick->GetNumberOfAxes()); + else if (strcmp(attrdef->m_name, "numHats") == 0) + return PyLong_FromLong(self->m_joystick->GetNumberOfHats()); + + // If we got here, we have a problem... + PyErr_SetString(PyExc_AttributeError, "invalid attribute"); + return NULL; +} + +PyObject* SCA_PythonJoystick::pyattr_get_active_buttons(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef) +{ + SCA_PythonJoystick* self = static_cast(self_v); + + int button_index = self->m_joystick->GetNumberOfButtons(); + + PyObject *list = PyList_New(0); + PyObject *value; + + for (int i=0; i < self->m_joystick->GetNumberOfButtons(); i++) { + if (self->m_joystick->aButtonPressIsPositive(i)) { + value = PyLong_FromSsize_t(i); + PyList_Append(list, value); + Py_DECREF(value); + } + } + + return list; +} + +PyObject* SCA_PythonJoystick::pyattr_get_hat_values(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef) +{ + SCA_PythonJoystick* self = static_cast(self_v); + + int hat_index = self->m_joystick->GetNumberOfHats(); + PyObject *list = PyList_New(hat_index); + + while (hat_index--) { + PyList_SET_ITEM(list, hat_index, PyLong_FromLong(self->m_joystick->GetHat(hat_index))); + } + + return list; +} + +PyObject* SCA_PythonJoystick::pyattr_get_axis_values(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef) +{ + SCA_PythonJoystick* self = static_cast(self_v); + + int axis_index = self->m_joystick->GetNumberOfAxes(); + PyObject *list = PyList_New(axis_index); + int position; + + while (axis_index--) { + position = self->m_joystick->GetAxisPosition(axis_index); + + // We get back a range from -32768 to 32767, so we use an if here to + // get a perfect -1.0 to 1.0 mapping. Some oddball system might have an + // actual min of -32767 for shorts, so we use SHRT_MIN/MAX to be safe. + if (position < 0) + PyList_SET_ITEM(list, axis_index, PyFloat_FromDouble(position/((double)-SHRT_MIN))); + else + PyList_SET_ITEM(list, axis_index, PyFloat_FromDouble(position/(double)SHRT_MAX)); + } + + return list; +} + +PyObject* SCA_PythonJoystick::pyattr_get_name(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef) +{ + SCA_PythonJoystick* self = static_cast(self_v); + + return PyUnicode_FromString(self->m_joystick->GetName()); +} +#endif diff --git a/source/gameengine/GameLogic/SCA_PythonJoystick.h b/source/gameengine/GameLogic/SCA_PythonJoystick.h new file mode 100644 index 00000000000..15c6285aed5 --- /dev/null +++ b/source/gameengine/GameLogic/SCA_PythonJoystick.h @@ -0,0 +1,56 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Mitchell Stokes. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file SCA_PythonJoystick.h + * \ingroup gamelogic + */ + +#ifndef __SCA_PYTHONJOYSTICK_H__ +#define __SCA_PYTHONJOYSTICK_H__ + +#include "PyObjectPlus.h" + +class SCA_PythonJoystick : public PyObjectPlus +{ + Py_Header +private: + class SCA_Joystick *m_joystick; +#ifdef WITH_PYTHON + PyObject* m_event_dict; +#endif +public: + SCA_PythonJoystick(class SCA_Joystick* joystick); + virtual ~SCA_PythonJoystick(); + +#ifdef WITH_PYTHON + virtual PyObject* py_repr(void); + + static PyObject* pyattr_get_num_x(void* self_v, const KX_PYATTRIBUTE_DEF *attrdef); + static PyObject* pyattr_get_active_buttons(void* self_v, const KX_PYATTRIBUTE_DEF *attrdef); + static PyObject* pyattr_get_hat_values(void* self_v, const KX_PYATTRIBUTE_DEF *attrdef); + static PyObject* pyattr_get_axis_values(void* self_v, const KX_PYATTRIBUTE_DEF *attrdef); + static PyObject* pyattr_get_name(void* self_v, const KX_PYATTRIBUTE_DEF *attrdef); +#endif +}; + +#endif //__SCA_PYTHONJOYSTICK_H__ + diff --git a/source/gameengine/Ketsji/KX_PythonInit.cpp b/source/gameengine/Ketsji/KX_PythonInit.cpp index 996be97c474..df8f6eb44e8 100644 --- a/source/gameengine/Ketsji/KX_PythonInit.cpp +++ b/source/gameengine/Ketsji/KX_PythonInit.cpp @@ -92,6 +92,8 @@ extern "C" { #include "SCA_PropertySensor.h" #include "SCA_RandomActuator.h" #include "SCA_KeyboardSensor.h" /* IsPrintable, ToCharacter */ +#include "SCA_JoystickManager.h" /* JOYINDEX_MAX */ +#include "SCA_PythonJoystick.h" #include "SCA_PythonKeyboard.h" #include "SCA_PythonMouse.h" #include "KX_ConstraintActuator.h" @@ -151,6 +153,7 @@ static char gp_GamePythonPathOrig[FILE_MAX] = ""; // not super happy about this, static SCA_PythonKeyboard* gp_PythonKeyboard = NULL; static SCA_PythonMouse* gp_PythonMouse = NULL; +static SCA_PythonJoystick* gp_PythonJoysticks[JOYINDEX_MAX] = {NULL}; #endif // WITH_PYTHON static KX_Scene* gp_KetsjiScene = NULL; @@ -1420,6 +1423,22 @@ PyObject *initGameLogic(KX_KetsjiEngine *engine, KX_Scene* scene) // quick hack gp_PythonMouse = new SCA_PythonMouse(gp_KetsjiEngine->GetMouseDevice(), gp_Canvas); PyDict_SetItemString(d, "mouse", gp_PythonMouse->NewProxy(true)); + PyObject* joylist = PyList_New(JOYINDEX_MAX); + SCA_JoystickManager* joyevent = (SCA_JoystickManager*)gp_KetsjiScene->GetLogicManager()->FindEventManager(SCA_EventManager::JOY_EVENTMGR); + for (int i=0; iGetJoystickDevice(i); + if (joy && joy->Connected()) { + gp_PythonJoysticks[i] = new SCA_PythonJoystick(joy); + PyObject* tmp = gp_PythonJoysticks[i]->NewProxy(true); + Py_INCREF(tmp); + PyList_SET_ITEM(joylist, i, tmp); + } else { + Py_INCREF(Py_None); + PyList_SET_ITEM(joylist, i, Py_None); + } + } + PyDict_SetItemString(d, "joysticks", joylist); + ErrorObject = PyUnicode_FromString("GameLogic.error"); PyDict_SetItemString(d, "error", ErrorObject); Py_DECREF(ErrorObject); @@ -1937,6 +1956,13 @@ void exitGamePlayerPythonScripting() delete gp_PythonMouse; gp_PythonMouse = NULL; + for (int i=0; i