// Filename: pythonCallbackObject.cxx // Created by: drose (13Mar09) // //////////////////////////////////////////////////////////////////// // // PANDA 3D SOFTWARE // Copyright (c) Carnegie Mellon University. All rights reserved. // // All use of this software is subject to the terms of the revised BSD // license. You should have received a copy of this license along // with this source code in a file named "LICENSE." // //////////////////////////////////////////////////////////////////// #include "pythonCallbackObject.h" #ifdef HAVE_PYTHON #include "py_panda.h" #include "thread.h" #include "callbackData.h" TypeHandle PythonCallbackObject::_type_handle; #ifndef CPPPARSER IMPORT_THIS struct Dtool_PyTypedObject Dtool_TypedObject; #endif //////////////////////////////////////////////////////////////////// // Function: PythonCallbackObject::Constructor // Access: Published // Description: //////////////////////////////////////////////////////////////////// PythonCallbackObject:: PythonCallbackObject(PyObject *function) { _function = Py_None; Py_INCREF(_function); set_function(function); #ifndef SIMPLE_THREADS // Ensure that the Python threading system is initialized and ready // to go. #ifdef WITH_THREAD // This symbol defined within Python.h PyEval_InitThreads(); #endif #endif } //////////////////////////////////////////////////////////////////// // Function: PythonCallbackObject::Destructor // Access: Published, Virtual // Description: //////////////////////////////////////////////////////////////////// PythonCallbackObject:: ~PythonCallbackObject() { Py_DECREF(_function); } //////////////////////////////////////////////////////////////////// // Function: PythonCallbackObject::set_function // Access: Published // Description: Replaces the function that is called for the callback. // runs. The parameter should be a Python callable // object. //////////////////////////////////////////////////////////////////// void PythonCallbackObject:: set_function(PyObject *function) { Py_DECREF(_function); _function = function; Py_INCREF(_function); if (_function != Py_None && !PyCallable_Check(_function)) { nassert_raise("Invalid function passed to PythonCallbackObject"); } } //////////////////////////////////////////////////////////////////// // Function: PythonCallbackObject::get_function // Access: Published // Description: Returns the function that is called for the callback. //////////////////////////////////////////////////////////////////// PyObject *PythonCallbackObject:: get_function() { Py_INCREF(_function); return _function; } //////////////////////////////////////////////////////////////////// // Function: PythonCallbackObject::do_callback // Access: Public, Virtual // Description: This method called when the callback is triggered; it // *replaces* the original function. To continue // performing the original function, you must call // cbdata->upcall() during the callback. //////////////////////////////////////////////////////////////////// void PythonCallbackObject:: do_callback(CallbackData *cbdata) { #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) // Use PyGILState to protect this asynchronous call. PyGILState_STATE gstate; gstate = PyGILState_Ensure(); #endif do_python_callback(cbdata); #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) PyGILState_Release(gstate); #endif } //////////////////////////////////////////////////////////////////// // Function: PythonCallbackObject::do_python_callback // Access: Private // Description: The Python calls that implement do_callback(). This // function is separate so we can acquire the Python // interpretor lock while it runs. //////////////////////////////////////////////////////////////////// void PythonCallbackObject:: do_python_callback(CallbackData *cbdata) { nassertv(cbdata != NULL); // Wrap the cbdata up in a Python object, then put it in a tuple, // for the argument list. PyObject *pycbdata = DTool_CreatePyInstanceTyped(cbdata, Dtool_TypedObject, false, false, cbdata->get_type_index()); PyObject *args = Py_BuildValue("(O)", pycbdata); Py_DECREF(pycbdata); PyObject *result = Thread::get_current_thread()->call_python_func(_function, args); Py_DECREF(args); if (result == (PyObject *)NULL) { util_cat.error() << "Exception occurred in " << *this << "\n"; } else { Py_DECREF(result); } } #endif // HAVE_PYTHON