///////////////////////////////////////////////////////////////////////////// // Name: helpers.cpp // Purpose: Helper functions/classes for the wxPython extension module // // Author: Robin Dunn // // Created: 7/1/97 // RCS-ID: $Id$ // Copyright: (c) 1998 by Total Control Software // Licence: wxWindows license ///////////////////////////////////////////////////////////////////////////// #include // get the correct definition of NULL #undef DEBUG #include #include "helpers.h" #ifdef __WXMSW__ #include #undef FindWindow #undef GetCharWidth #undef LoadAccelerators #undef GetClassInfo #undef GetClassName #endif #ifdef __WXGTK__ #include #include #include //#include //#include //#include //extern GtkWidget *wxRootWindow; #endif #ifdef __WXMSW__ // If building for win32... //---------------------------------------------------------------------- // This gets run when the DLL is loaded. We just need to save a handle. //---------------------------------------------------------------------- BOOL WINAPI DllMain( HINSTANCE hinstDLL, // handle to DLL module DWORD fdwReason, // reason for calling function LPVOID lpvReserved // reserved ) { wxSetInstance(hinstDLL); return 1; } #endif //---------------------------------------------------------------------- // Class for implementing the wxp main application shell. //---------------------------------------------------------------------- wxPyApp *wxPythonApp = NULL; // Global instance of application object wxPyApp::wxPyApp() { // printf("**** ctor\n"); } wxPyApp::~wxPyApp() { // printf("**** dtor\n"); } // This one isn't acutally called... See __wxStart() bool wxPyApp::OnInit(void) { return FALSE; } int wxPyApp::MainLoop(void) { int retval = 0; DeletePendingObjects(); #ifdef __WXGTK__ m_initialized = wxTopLevelWindows.GetCount() != 0; #endif if (Initialized()) { retval = wxApp::MainLoop(); wxPythonApp->OnExit(); } return retval; } //--------------------------------------------------------------------- //---------------------------------------------------------------------- #ifdef __WXMSW__ #include "wx/msw/msvcrt.h" #endif int WXDLLEXPORT wxEntryStart( int argc, char** argv ); int WXDLLEXPORT wxEntryInitGui(); void WXDLLEXPORT wxEntryCleanup(); #ifdef WXP_WITH_THREAD PyThreadState* wxPyEventThreadState = NULL; #endif // This is where we pick up the first part of the wxEntry functionality... // The rest is in __wxStart and __wxCleanup. This function is called when // wxcmodule is imported. (Before there is a wxApp object.) void __wxPreStart() { #ifdef __WXMSW__ // wxCrtSetDbgFlag(_CRTDBG_LEAK_CHECK_DF); #endif #ifdef WXP_WITH_THREAD #if 0 // OLD THREAD STUFF PyEval_InitThreads(); wxPyEventThreadState = PyThreadState_Get(); #else PyEval_InitThreads(); wxPyEventThreadState = PyThreadState_New(PyThreadState_Get()->interp); #endif #endif // Bail out if there is already windows created. This means that the // toolkit has already been initialized, as in embedding wxPython in // a C++ wxWindows app. if (wxTopLevelWindows.Number() > 0) return; PyObject* sysargv = PySys_GetObject("argv"); int argc = PyList_Size(sysargv); char** argv = new char*[argc+1]; int x; for(x=0; x 0) { PyErr_SetString(PyExc_TypeError, "Only 1 wxApp per process!"); return NULL; } #endif // This is the next part of the wxEntry functionality... PyObject* sysargv = PySys_GetObject("argv"); int argc = PyList_Size(sysargv); char** argv = new char*[argc+1]; int x; for(x=0; xargc = argc; wxPythonApp->argv = argv; wxEntryInitGui(); // Call the Python App's OnInit function arglist = PyTuple_New(0); result = PyEval_CallObject(onInitFunc, arglist); if (!result) { // an exception was raised. return NULL; } if (! PyInt_Check(result)) { PyErr_SetString(PyExc_TypeError, "OnInit should return a boolean value"); return NULL; } bResult = PyInt_AS_LONG(result); if (! bResult) { PyErr_SetString(PyExc_SystemExit, "OnInit returned FALSE, exiting..."); return NULL; } #ifdef __WXGTK__ wxTheApp->m_initialized = (wxTopLevelWindows.GetCount() > 0); #endif Py_INCREF(Py_None); return Py_None; } void __wxCleanup() { wxEntryCleanup(); } PyObject* wxPython_dict; PyObject* __wxSetDictionary(PyObject* /* self */, PyObject* args) { if (!PyArg_ParseTuple(args, "O", &wxPython_dict)) return NULL; if (!PyDict_Check(wxPython_dict)) { PyErr_SetString(PyExc_TypeError, "_wxSetDictionary must have dictionary object!"); return NULL; } #ifdef __WXMOTIF__ #define wxPlatform "__WXMOTIF__" #endif #ifdef __WXQT__ #define wxPlatform "__WXQT__" #endif #ifdef __WXGTK__ #define wxPlatform "__WXGTK__" #endif #if defined(__WIN32__) || defined(__WXMSW__) #define wxPlatform "__WXMSW__" #endif #ifdef __WXMAC__ #define wxPlatform "__WXMAC__" #endif PyDict_SetItemString(wxPython_dict, "wxPlatform", PyString_FromString(wxPlatform)); Py_INCREF(Py_None); return Py_None; } //--------------------------------------------------------------------------- PyObject* wxPyConstructObject(void* ptr, const char* className, int setThisOwn) { PyObject* obj; PyObject* arg; if (!ptr) { Py_INCREF(Py_None); return Py_None; } char buff[64]; // should always be big enough... char swigptr[64]; sprintf(buff, "_%s_p", className); SWIG_MakePtr(swigptr, ptr, buff); sprintf(buff, "%sPtr", className); PyObject* classobj = PyDict_GetItemString(wxPython_dict, buff); if (! classobj) { //Py_INCREF(Py_None); //return Py_None; char temp[128]; sprintf(temp, "*** Unknown class name %s, tell Robin about it please ***", buff); obj = PyString_FromString(temp); return obj; } arg = Py_BuildValue("(s)", swigptr); obj = PyInstance_New(classobj, arg, NULL); Py_DECREF(arg); if (setThisOwn) { PyObject* one = PyInt_FromLong(1); PyObject_SetAttrString(obj, "thisown", one); Py_DECREF(one); } return obj; } //--------------------------------------------------------------------------- static PyThreadState* myPyThreadState_Get() { PyThreadState* current; current = PyThreadState_Swap(NULL); PyThreadState_Swap(current); return current; } bool wxPyRestoreThread() { // NOTE: The Python API docs state that if a thread already has the // interpreter lock and calls PyEval_RestoreThread again a deadlock // occurs, so I put in this code as a guard condition since there are // many possibilites for nested events and callbacks in wxPython. If // The current thread is our thread, then we can assume that we // already have the lock. (I hope!) // #ifdef WXP_WITH_THREAD #if 0 // OLD THREAD STUFF if (wxPyEventThreadState != myPyThreadState_Get()) { PyEval_RestoreThread(wxPyEventThreadState); return TRUE; } else #else if (wxPyEventThreadState != myPyThreadState_Get()) { PyEval_AcquireThread(wxPyEventThreadState); return TRUE; } else #endif #endif return FALSE; } void wxPySaveThread(bool doSave) { #ifdef WXP_WITH_THREAD #if 0 // OLD THREAD STUFF if (doSave) { wxPyEventThreadState = PyEval_SaveThread(); } #else if (doSave) { PyEval_ReleaseThread(wxPyEventThreadState); } #endif #endif } //--------------------------------------------------------------------------- IMPLEMENT_ABSTRACT_CLASS(wxPyCallback, wxObject); wxPyCallback::wxPyCallback(PyObject* func) { m_func = func; Py_INCREF(m_func); } wxPyCallback::wxPyCallback(const wxPyCallback& other) { m_func = other.m_func; Py_INCREF(m_func); } wxPyCallback::~wxPyCallback() { bool doSave = wxPyRestoreThread(); Py_DECREF(m_func); wxPySaveThread(doSave); } // This function is used for all events destined for Python event handlers. void wxPyCallback::EventThunker(wxEvent& event) { wxPyCallback* cb = (wxPyCallback*)event.m_callbackUserData; PyObject* func = cb->m_func; PyObject* result; PyObject* arg; PyObject* tuple; bool doSave = wxPyRestoreThread(); wxString className = event.GetClassInfo()->GetClassName(); if (className == "wxPyEvent") arg = ((wxPyEvent*)&event)->GetSelf(); else if (className == "wxPyCommandEvent") arg = ((wxPyCommandEvent*)&event)->GetSelf(); else arg = wxPyConstructObject((void*)&event, className); tuple = PyTuple_New(1); PyTuple_SET_ITEM(tuple, 0, arg); result = PyEval_CallObject(func, tuple); Py_DECREF(tuple); if (result) { Py_DECREF(result); PyErr_Clear(); } else { PyErr_Print(); } wxPySaveThread(doSave); } //---------------------------------------------------------------------- wxPyCallbackHelper::wxPyCallbackHelper(const wxPyCallbackHelper& other) { m_lastFound = NULL; m_self = other.m_self; m_class = other.m_class; if (m_self) { Py_INCREF(m_self); Py_INCREF(m_class); } } void wxPyCallbackHelper::setSelf(PyObject* self, PyObject* klass, int incref) { m_self = self; m_class = klass; m_incRef = incref; if (incref) { Py_INCREF(m_self); Py_INCREF(m_class); } } // If the object (m_self) has an attibute of the given name, and if that // attribute is a method, and if that method's class is not from a base class, // then we'll save a pointer to the method so callCallback can call it. bool wxPyCallbackHelper::findCallback(const char* name) const { wxPyCallbackHelper* self = (wxPyCallbackHelper*)this; // cast away const self->m_lastFound = NULL; if (m_self && PyObject_HasAttrString(m_self, (char*)name)) { PyObject* method; method = PyObject_GetAttrString(m_self, (char*)name); if (PyMethod_Check(method) && ((PyMethod_GET_CLASS(method) == m_class) || PyClass_IsSubclass(PyMethod_GET_CLASS(method), m_class))) { self->m_lastFound = method; } else { Py_DECREF(method); } } return m_lastFound != NULL; } int wxPyCallbackHelper::callCallback(PyObject* argTuple) const { PyObject* result; int retval = FALSE; result = callCallbackObj(argTuple); if (result) { // Assumes an integer return type... retval = PyInt_AsLong(result); Py_DECREF(result); PyErr_Clear(); // forget about it if it's not... } return retval; } // Invoke the Python callable object, returning the raw PyObject return // value. Caller should DECREF the return value and also call PyEval_SaveThread. PyObject* wxPyCallbackHelper::callCallbackObj(PyObject* argTuple) const { PyObject* result; // Save a copy of the pointer in case the callback generates another // callback. In that case m_lastFound will have a different value when // it gets back here... PyObject* method = m_lastFound; result = PyEval_CallObject(method, argTuple); Py_DECREF(argTuple); Py_DECREF(method); if (!result) { PyErr_Print(); } return result; } void wxPyCBH_setSelf(wxPyCallbackHelper& cbh, PyObject* self, PyObject* klass, int incref) { cbh.setSelf(self, klass, incref); } bool wxPyCBH_findCallback(const wxPyCallbackHelper& cbh, const char* name) { return cbh.findCallback(name); } int wxPyCBH_callCallback(const wxPyCallbackHelper& cbh, PyObject* argTuple) { return cbh.callCallback(argTuple); } PyObject* wxPyCBH_callCallbackObj(const wxPyCallbackHelper& cbh, PyObject* argTuple) { return cbh.callCallbackObj(argTuple); } void wxPyCBH_delete(wxPyCallbackHelper* cbh) { bool doSave = wxPyRestoreThread(); if (cbh->m_incRef) { Py_XDECREF(cbh->m_self); Py_XDECREF(cbh->m_class); } wxPySaveThread(doSave); } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // These classes can be derived from in Python and passed through the event // system without losing anything. They do this by keeping a reference to // themselves and some special case handling in wxPyCallback::EventThunker. wxPyEvtSelfRef::wxPyEvtSelfRef() { //m_self = Py_None; // **** We don't do normal ref counting to prevent //Py_INCREF(m_self); // circular loops... m_cloned = FALSE; } wxPyEvtSelfRef::~wxPyEvtSelfRef() { bool doSave = wxPyRestoreThread(); if (m_cloned) Py_DECREF(m_self); wxPySaveThread(doSave); } void wxPyEvtSelfRef::SetSelf(PyObject* self, bool clone) { bool doSave = wxPyRestoreThread(); if (m_cloned) Py_DECREF(m_self); m_self = self; if (clone) { Py_INCREF(m_self); m_cloned = TRUE; } wxPySaveThread(doSave); } PyObject* wxPyEvtSelfRef::GetSelf() const { Py_INCREF(m_self); return m_self; } wxPyEvent::wxPyEvent(int id) : wxEvent(id) { } wxPyEvent::~wxPyEvent() { } // This one is so the event object can be Cloned... void wxPyEvent::CopyObject(wxObject& dest) const { wxEvent::CopyObject(dest); ((wxPyEvent*)&dest)->SetSelf(m_self, TRUE); } IMPLEMENT_DYNAMIC_CLASS(wxPyEvent, wxEvent); wxPyCommandEvent::wxPyCommandEvent(wxEventType commandType, int id) : wxCommandEvent(commandType, id) { } wxPyCommandEvent::~wxPyCommandEvent() { } void wxPyCommandEvent::CopyObject(wxObject& dest) const { wxCommandEvent::CopyObject(dest); ((wxPyCommandEvent*)&dest)->SetSelf(m_self, TRUE); } IMPLEMENT_DYNAMIC_CLASS(wxPyCommandEvent, wxCommandEvent); //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- wxPyTimer::wxPyTimer(PyObject* callback) { func = callback; Py_INCREF(func); } wxPyTimer::~wxPyTimer() { bool doSave = wxPyRestoreThread(); Py_DECREF(func); wxPySaveThread(doSave); } void wxPyTimer::Notify() { if (!func || func == Py_None) { wxTimer::Notify(); } else { bool doSave = wxPyRestoreThread(); PyObject* result; PyObject* args = Py_BuildValue("()"); result = PyEval_CallObject(func, args); Py_DECREF(args); if (result) { Py_DECREF(result); PyErr_Clear(); } else { PyErr_Print(); } wxPySaveThread(doSave); } } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // Convert a wxList to a Python List PyObject* wxPy_ConvertList(wxListBase* list, const char* className) { PyObject* pyList; PyObject* pyObj; wxObject* wxObj; wxNode* node = list->First(); bool doSave = wxPyRestoreThread(); pyList = PyList_New(0); while (node) { wxObj = node->Data(); pyObj = wxPyConstructObject(wxObj, className); PyList_Append(pyList, pyObj); node = node->Next(); } wxPySaveThread(doSave); return pyList; } //---------------------------------------------------------------------- long wxPyGetWinHandle(wxWindow* win) { #ifdef __WXMSW__ return (long)win->GetHandle(); #endif // Find and return the actual X-Window. #ifdef __WXGTK__ if (win->m_wxwindow) { GdkWindowPrivate* bwin = (GdkWindowPrivate*)GTK_PIZZA(win->m_wxwindow)->bin_window; if (bwin) { return (long)bwin->xwindow; } } #endif return 0; } //---------------------------------------------------------------------- // Some helper functions for typemaps in my_typemaps.i, so they won't be // included in every file... byte* byte_LIST_helper(PyObject* source) { if (!PyList_Check(source)) { PyErr_SetString(PyExc_TypeError, "Expected a list object."); return NULL; } int count = PyList_Size(source); byte* temp = new byte[count]; if (! temp) { PyErr_SetString(PyExc_MemoryError, "Unable to allocate temporary array"); return NULL; } for (int x=0; xx = PyInt_AS_LONG(o1); point->y = PyInt_AS_LONG(o2); return true; } if (PyFloat_Check(o1) && PyFloat_Check(o2)) { point->x = (int)PyFloat_AS_DOUBLE(o1); point->y = (int)PyFloat_AS_DOUBLE(o2); return true; } if (PyInstance_Check(o1) || PyInstance_Check(o2)) { // Disallow instances because they can cause havok return false; } if (PyNumber_Check(o1) && PyNumber_Check(o2)) { // I believe this excludes instances, so this should be safe without INCREFFing o1 and o2 point->x = PyInt_AsLong(o1); point->y = PyInt_AsLong(o2); return true; } return false; } #if PYTHON_API_VERSION < 1009 #define PySequence_Fast_GET_ITEM(o, i)\ (PyList_Check(o) ? PyList_GET_ITEM(o, i) : PyTuple_GET_ITEM(o, i)) #endif wxPoint* wxPoint_LIST_helper(PyObject* source, int *count) { // Putting all of the declarations here allows // us to put the error handling all in one place. int x; wxPoint* temp; PyObject *o, *o1, *o2; int isFast = PyList_Check(source) || PyTuple_Check(source); // The length of the sequence is returned in count. if (!PySequence_Check(source)) { goto error0; } *count = PySequence_Length(source); if (*count < 0) { goto error0; } temp = new wxPoint[*count]; if (!temp) { PyErr_SetString(PyExc_MemoryError, "Unable to allocate temporary array"); return NULL; } for (x=0; x<*count; x++) { // Get an item: try fast way first. if (isFast) { o = PySequence_Fast_GET_ITEM(source, x); } else { o = PySequence_GetItem(source, x); if (o == NULL) { goto error1; } } // Convert o to wxPoint. if ((PyTuple_Check(o) && PyTuple_GET_SIZE(o) == 2) || (PyList_Check(o) && PyList_GET_SIZE(o) == 2)) { o1 = PySequence_Fast_GET_ITEM(o, 0); o2 = PySequence_Fast_GET_ITEM(o, 1); if (!wxPointFromObjects(o1, o2, &temp[x])) { goto error2; } } else if (PyInstance_Check(o)) { wxPoint* pt; if (SWIG_GetPtrObj(o, (void **)&pt, "_wxPoint_p")) { goto error2; } temp[x] = *pt; } else if (PySequence_Check(o) && PySequence_Length(o) == 2) { o1 = PySequence_GetItem(o, 0); o2 = PySequence_GetItem(o, 1); if (!wxPointFromObjects(o1, o2, &temp[x])) { goto error3; } Py_DECREF(o1); Py_DECREF(o2); } else { goto error2; } // Clean up. if (!isFast) Py_DECREF(o); } return temp; error3: Py_DECREF(o1); Py_DECREF(o2); error2: if (!isFast) Py_DECREF(o); error1: delete temp; error0: PyErr_SetString(PyExc_TypeError, "Expected a sequence of length-2 sequences or wxPoints."); return NULL; } // end of patch //------------------------------ wxBitmap** wxBitmap_LIST_helper(PyObject* source) { if (!PyList_Check(source)) { PyErr_SetString(PyExc_TypeError, "Expected a list object."); return NULL; } int count = PyList_Size(source); wxBitmap** temp = new wxBitmap*[count]; if (! temp) { PyErr_SetString(PyExc_MemoryError, "Unable to allocate temporary array"); return NULL; } for (int x=0; x