/* * The SIP library code that implements the interface to the optional module * supplied Qt support. * * Copyright (c) 2007 * Riverbank Computing Limited * * This file is part of SIP. * * This copy of SIP is licensed for use under the terms of the SIP License * Agreement. See the file LICENSE for more details. * * SIP is supplied WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ #include #include #include "sip.h" #include "sipint.h" /* This is how Qt "types" signals and slots. */ #define isQtSlot(s) (*(s) == '1') #define isQtSignal(s) (*(s) == '2') static PyObject *py_sender = NULL; /* The last Python signal sender. */ static int isSameSlot(sipSlot *,PyObject *,const char *); static int emitQtSig(sipWrapper *,const char *,PyObject *); static int emitToSlotList(sipSlotList *rxlist, PyObject *sigargs); static int addSlotToPySigList(sipWrapper *,const char *,PyObject *,const char *); static void removeSlotFromPySigList(sipWrapper *,const char *,PyObject *,const char *); static PyObject *getWeakRef(PyObject *obj); static sipPySig *findPySignal(sipWrapper *,const char *); static char *sipStrdup(const char *); static int saveSlot(sipSlot *sp, PyObject *rxObj, const char *slot); static void *createUniversalSlot(sipWrapper *txSelf, const char *sig, PyObject *rxObj, const char *slot, const char **member); static void *findSignal(void *txrx, const char **sig); static void *newSignal(void *txrx, const char **sig); static void freeSlot(sipSlot *slot); /* * Return the most recent signal sender. */ PyObject *sip_api_get_sender() { PyObject *sender; const void *qt_sender; /* * If there is a Qt sender then it is more recent than the last Python * sender, so use it instead. */ if ((qt_sender = sipQtSupport->qt_get_sender()) != NULL) sender = sip_api_convert_from_instance((void *)qt_sender, sipQObjectClass, NULL); else { if ((sender = py_sender) == NULL) sender = Py_None; Py_INCREF(sender); } return sender; } /* * Release the resources held by a connection. */ void sip_api_free_connection(sipSlotConnection *conn) { freeSlot(&conn->sc_slot); } /* * Compare two connections and return TRUE if they are the same. */ int sip_api_same_connection(sipSlotConnection *conn, void *tx, const char *sig, PyObject *rxObj, const char *slot) { return (conn->sc_transmitter == tx && sipQtSupport->qt_same_name(conn->sc_signature->sg_signature, sig) && isSameSlot(&conn->sc_slot, rxObj, slot)); } /* * Parse the signal arguments for a connection. */ sipSignature *sip_api_parse_signature(const char *sig) { static sipSignature *psig_list = NULL; sipSignature *psig; const char *sp, *ep; /* * First see if it has already been parsed. Note that both sides of a * connection will probably be parsed twice because the function names will * be different even though the signatures will probably be the same. We * could be more clever, the most saving is when repeatedly emitting a * signal for which this is sufficient. */ for (psig = psig_list; psig != NULL; psig = psig->sg_next) if (sipQtSupport->qt_same_name(psig->sg_signature, sig)) return psig; /* Create a new one including space for the copy of the signature. */ if ((psig = (sipSignature *)sip_api_malloc(sizeof (sipSignature) + strlen(sig) + 1)) == NULL) return NULL; psig->sg_signature = (char *)&psig[1]; psig->sg_nrargs = 0; psig->sg_args = 0; /* Find the start and end of the arguments. */ sp = strchr(sig, '('); ep = strrchr(sig, ')'); /* If the signal isn't well formed we assume Qt will pick it up. */ if (sp && ep && sp < ep) { /* * Copy the signature arguments while counting them and * removing non-significant spaces. Each argument is left as a * '\0' terminated string. */ char *dp = psig->sg_signature; int depth = 0, nrcommas = 0, argstart = TRUE; for (;;) { char ch = *++sp; if (strchr(",*&)<>", ch)) { /* Backup over any previous trailing space. */ if (dp > psig->sg_signature && dp[-1] == ' ') --dp; if (sp == ep) { *dp = '\0'; break; } if (ch == ',' && depth == 0) { *dp++ = '\0'; ++nrcommas; argstart = TRUE; } else { *dp++ = ch; /* * Make sure commas in template * arguments are ignored. */ if (ch == '<') ++depth; else if (ch == '>') --depth; } } else if (ch == ' ') { /* Ignore leading and multiple spaces. */ if (!argstart && dp[-1] != ' ') *dp++ = ch; } else { *dp++ = ch; argstart = FALSE; } } /* Handle the arguments now they are in a normal form. */ if (*psig->sg_signature) { char *arg = psig->sg_signature; int a; /* Allocate the space. */ psig->sg_nrargs = nrcommas + 1; if ((psig->sg_args = (sipSigArg *)sip_api_malloc(sizeof (sipSigArg) * psig->sg_nrargs)) == NULL) { sip_api_free(psig); return NULL; } for (a = 0; a < psig->sg_nrargs; ++a) { size_t btlen = 0; int unsup, isref = FALSE, indir = 0; sipSigArgType sat = unknown_sat; /* Find the start of the significant part of the type. */ dp = arg; if (strncmp(dp, "const ", 6) == 0) dp += 6; /* * Find the length of the base type, the number of indirections * and if it is a reference. */ for (ep = dp; *ep; ++ep) if (*ep == '&') isref = TRUE; else if (*ep == '*') ++indir; else ++btlen; /* * Assume that anything other than a base type is unsupported. */ unsup = (isref || indir); /* Parse the base type. */ switch (btlen) { case 3: if (strncmp(dp, "int", 3) == 0) sat = int_sat; break; case 4: if (strncmp(dp, "bool", 4) == 0) sat = bool_sat; else if (strncmp(dp, "long", 4) == 0) sat = long_sat; else if (strncmp(dp, "char", 4) == 0) { sat = (indir ? string_sat : char_sat); unsup = (isref || indir > 1); } else if (strncmp(dp, "void", 4) == 0) { sat = void_sat; unsup = (isref || indir != 1); } break; case 5: if (strncmp(dp, "float", 5) == 0) sat = float_sat; else if (strncmp(dp, "short", 5) == 0) sat = short_sat; break; case 6: if (strncmp(dp, "double", 6) == 0) sat = double_sat; break; case 7: if (strncmp(dp, "__int64", 7) == 0) sat = longlong_sat; else if (strncmp(dp, "wchar_t", 7) == 0) { sat = (indir ? wstring_sat : wchar_sat); unsup = (isref || indir > 1); } break; case 8: if (strncmp(dp, "unsigned", 8) == 0) sat = uint_sat; else if (strncmp(dp, "QVariant", 8) == 0) { if (indir == 0) { sat = qvariant_sat; unsup = FALSE; } else if (indir == 1) { sat = qvariantp_sat; unsup = FALSE; } } break; case 9: if (strncmp(dp, "long long", 9) == 0) sat = longlong_sat; break; case 11: if (strncmp(dp, "signed char", 11) == 0) { sat = (indir ? sstring_sat : schar_sat); unsup = (isref || indir > 1); } break; case 12: if (strncmp(dp, "unsigned int", 12) == 0) sat = uint_sat; break; case 13: if (strncmp(dp, "unsigned long", 13) == 0) sat = ulong_sat; else if (strncmp(dp, "unsigned char", 13) == 0) { sat = (indir ? ustring_sat : uchar_sat); unsup = (isref || indir > 1); } else if (strncmp(dp, "PyQt_PyObject", 13) == 0 && indir == 0) { sat = pyobject_sat; unsup = FALSE; } break; case 14: if (strncmp(dp, "unsigned short", 14) == 0) sat = ushort_sat; break; case 16: if (strncmp(dp, "unsigned __int64", 16) == 0) sat = ulonglong_sat; break; case 18: if (strncmp(dp, "unsigned long long", 18) == 0) sat = ulonglong_sat; break; } if (sat == unknown_sat) sipFindSigArgType(dp, btlen, &psig->sg_args[a], indir); else { if (unsup) sat = unknown_sat; psig->sg_args[a].atype = sat; } /* Move to the start of the next argument. */ arg += strlen(arg) + 1; } } } /* Make a deep copy of the signal. */ strcpy(psig->sg_signature, sig); /* Add it to the list so it can be re-used. */ psig->sg_next = psig_list; psig_list = psig; return psig; } /* * Find an existing signal. */ static void *findSignal(void *txrx, const char **sig) { sipSignature *psig; /* * Handle the trivial case where the Qt implementation doesn't support * universal signals. */ if (sipQtSupport->qt_is_qt_signal == NULL) return txrx; /* See if this a shortcircuited Python signal. */ if (strchr(*sig, '(') == NULL) return sipQtSupport->qt_find_universal_signal_shortcut(txrx, *sig, sig); /* See if the existing object can be used itself. */ if (sipQtSupport->qt_is_qt_signal(txrx, *sig)) return txrx; if ((psig = sip_api_parse_signature(*sig)) == NULL) return NULL; /* Find an ordinary universal signal. */ return sipQtSupport->qt_find_universal_signal(txrx, psig); } /* * Return a usable signal, creating a new universal signal if needed. */ static void *newSignal(void *txrx, const char **sig) { sipSignature *psig; /* * Handle the trivial case where the Qt implementation doesn't support * universal signals. */ if (sipQtSupport->qt_is_qt_signal == NULL) return txrx; /* See if this a shortcircuited Python signal. */ if (strchr(*sig, '(') == NULL) return sipQtSupport->qt_create_universal_signal_shortcut(txrx, *sig, sig); /* See if the existing object can be used itself. */ if (sipQtSupport->qt_is_qt_signal(txrx, *sig)) return txrx; if ((psig = sip_api_parse_signature(*sig)) == NULL) return NULL; /* Create an ordinary universal signal. */ return sipQtSupport->qt_create_universal_signal(txrx, psig); } /* * Create a universal slot. Returns a pointer to it or 0 if there was an * error. */ static void *createUniversalSlot(sipWrapper *txSelf, const char *sig, PyObject *rxObj, const char *slot, const char **member) { sipSlotConnection conn; void *us; /* Initialise the connection. */ conn.sc_transmitter = (txSelf ? sipGetAddress(txSelf) : 0); /* Save the real slot. */ if (saveSlot(&conn.sc_slot, rxObj, slot) < 0) return 0; /* Parse the signature and create the universal slot. */ if ((conn.sc_signature = sip_api_parse_signature(sig)) == NULL || (us = sipQtSupport->qt_create_universal_slot(txSelf, &conn, member)) == NULL) { sip_api_free_connection(&conn); return 0; } return us; } /* * Emit a Python or Qt signal. */ int sip_api_emit_signal(PyObject *self,const char *sig,PyObject *sigargs) { sipPySig *ps; void *tx; sipWrapper *w = (sipWrapper *)self; /* * Don't do anything if signals are blocked. Qt signals would be blocked * anyway, but this blocks Python signals as well. */ if ((tx = sip_api_get_cpp_ptr(w, sipQObjectClass)) == NULL || sipQtSupport->qt_signals_blocked(tx)) return 0; if (isQtSignal(sig)) { sipSignature *psig; /* Handle Qt implementations that emit using generated code. */ if (!sipQtSupport->qt_emit_signal) return emitQtSig(w, sig, sigargs); /* See if the signal is a shortcut. */ if (strchr(sig, '(') == NULL) return sipQtSupport->qt_emit_signal_shortcut(tx, sig, sigargs); if ((psig = sip_api_parse_signature(sig)) == NULL) return -1; if (psig->sg_nrargs != PyTuple_GET_SIZE(sigargs)) PyErr_Format(PyExc_TypeError, "Signal has %d arguments, but %d given", psig->sg_nrargs, PyTuple_GET_SIZE(sigargs)); return sipQtSupport->qt_emit_signal(tx, psig, sigargs); } if ((ps = findPySignal(w,sig)) != NULL) { int rc; /* Forget the last Qt sender and remember this one. */ sipQtSupport->qt_forget_sender(); py_sender = self; rc = emitToSlotList(ps -> rxlist,sigargs); /* Forget this as a sender. */ py_sender = NULL; return rc; } return 0; } /* * Search the Python signal list for a signal. */ static sipPySig *findPySignal(sipWrapper *w,const char *sig) { sipPySig *ps; for (ps = w -> pySigList; ps != NULL; ps = ps -> next) if (sipQtSupport->qt_same_name(ps -> name,sig)) return ps; return NULL; } /* * Search a signal table for a signal. If found, call the emitter function * with the signal arguments. Return 0 if the signal was emitted or <0 if * there was an error. */ static int emitQtSig(sipWrapper *w,const char *sig,PyObject *sigargs) { sipQtSignal *tab; /* Search the table. */ for (tab = ((sipWrapperType *)(w -> ob_type)) -> type -> td_emit; tab -> st_name != NULL; ++tab) { const char *sp, *tp; int found; /* Compare only the base name. */ sp = &sig[1]; tp = tab -> st_name; found = TRUE; while (*sp != '\0' && *sp != '(' && *tp != '\0') if (*sp++ != *tp++) { found = FALSE; break; } if (found) return (*tab -> st_emitfunc)(w,sigargs); } /* It wasn't found if we got this far. */ PyErr_Format(PyExc_NameError,"Invalid signal %s",&sig[1]); return -1; } /* * Send a signal to a single slot (Qt or Python). */ int sip_api_emit_to_slot(sipSlot *slot, PyObject *sigargs) { PyObject *sa, *oxtype, *oxvalue, *oxtb, *sfunc, *newmeth, *sref; /* Keep some compilers quiet. */ oxtype = oxvalue = oxtb = NULL; /* Fan out Qt signals. */ if (slot -> name != NULL && slot -> name[0] != '\0') return sip_api_emit_signal(slot -> pyobj,slot -> name,sigargs); /* Get the object to call, resolving any weak references. */ if (slot -> weakSlot == NULL) sref = NULL; else if ((sref = PyWeakref_GetObject(slot -> weakSlot)) == NULL) return -1; else Py_INCREF(sref); if (sref == Py_None) { /* * If the real object has gone then we pretend everything is Ok. This * mimics the Qt behaviour of not caring if a receiving object has been * deleted. */ Py_DECREF(sref); return 0; } if (slot -> pyobj == NULL) { PyObject *self = (sref != NULL ? sref : slot->meth.mself); /* See if any underlying C++ instance has gone. */ if (self != NULL && sip_api_wrapper_check(self) && ((sipWrapper *)self)->u.cppPtr == NULL) { Py_XDECREF(sref); return 0; } if ((sfunc = PyMethod_New(slot->meth.mfunc, self, slot->meth.mclass)) == NULL) { Py_XDECREF(sref); return -1; } /* Make sure we garbage collect the new method. */ newmeth = sfunc; } else if (slot -> name != NULL) { char *mname = slot -> name + 1; PyObject *self = (sref != NULL ? sref : slot->pyobj); /* See if any underlying C++ instance has gone. */ if (self != NULL && sip_api_wrapper_check(self) && ((sipWrapper *)self)->u.cppPtr == NULL) { Py_XDECREF(sref); return 0; } if ((sfunc = PyObject_GetAttrString(self, mname)) == NULL || !PyCFunction_Check(sfunc)) { /* * Note that in earlier versions of SIP this error would be * detected when the slot was connected. */ PyErr_Format(PyExc_NameError,"Invalid slot %s",mname); Py_XDECREF(sref); return -1; } /* Make sure we garbage collect the new method. */ newmeth = sfunc; } else if (slot->pyobj == Py_None) { /* * This was a lambda function that has been freed by the cyclic garbage * collector so ignore it. */ Py_XDECREF(sref); return 0; } else { sfunc = slot -> pyobj; newmeth = NULL; } /* * We make repeated attempts to call a slot. If we work out that it failed * because of an immediate type error we try again with one less argument. * We keep going until we run out of arguments to drop. This emulates the * Qt ability of the slot to accept fewer arguments than a signal provides. */ sa = sigargs; Py_INCREF(sa); for (;;) { PyObject *nsa, *xtype, *xvalue, *xtb, *resobj; if ((resobj = PyEval_CallObject(sfunc,sa)) != NULL) { Py_DECREF(resobj); Py_XDECREF(newmeth); Py_XDECREF(sref); /* Remove any previous exception. */ if (sa != sigargs) { Py_XDECREF(oxtype); Py_XDECREF(oxvalue); Py_XDECREF(oxtb); PyErr_Clear(); } Py_DECREF(sa); return 0; } /* Get the exception. */ PyErr_Fetch(&xtype,&xvalue,&xtb); /* * See if it is unacceptable. An acceptable failure is a type error * with no traceback - so long as we can still reduce the number of * arguments and try again. */ if (!PyErr_GivenExceptionMatches(xtype,PyExc_TypeError) || xtb != NULL || PyTuple_GET_SIZE(sa) == 0) { /* * If there is a traceback then we must have called the slot and * the exception was later on - so report the exception as is. */ if (xtb != NULL) { if (sa != sigargs) { Py_XDECREF(oxtype); Py_XDECREF(oxvalue); Py_XDECREF(oxtb); } PyErr_Restore(xtype,xvalue,xtb); } else if (sa == sigargs) PyErr_Restore(xtype,xvalue,xtb); else { /* * Discard the latest exception and restore the original one. */ Py_XDECREF(xtype); Py_XDECREF(xvalue); Py_XDECREF(xtb); PyErr_Restore(oxtype,oxvalue,oxtb); } break; } /* If this is the first attempt, save the exception. */ if (sa == sigargs) { oxtype = xtype; oxvalue = xvalue; oxtb = xtb; } else { Py_XDECREF(xtype); Py_XDECREF(xvalue); Py_XDECREF(xtb); } /* Create the new argument tuple. */ if ((nsa = PyTuple_GetSlice(sa,0,PyTuple_GET_SIZE(sa) - 1)) == NULL) { /* Tidy up. */ Py_XDECREF(oxtype); Py_XDECREF(oxvalue); Py_XDECREF(oxtb); break; } Py_DECREF(sa); sa = nsa; } Py_XDECREF(newmeth); Py_XDECREF(sref); Py_DECREF(sa); return -1; } /* * Send a signal to the slots (Qt or Python) in a Python list. */ static int emitToSlotList(sipSlotList *rxlist,PyObject *sigargs) { int rc; /* Apply the arguments to each slot method. */ rc = 0; while (rxlist != NULL && rc >= 0) { sipSlotList *next; /* * We get the next in the list before calling the slot in case the list * gets changed by the slot - usually because the slot disconnects * itself. */ next = rxlist -> next; rc = sip_api_emit_to_slot(&rxlist -> rx, sigargs); rxlist = next; } return rc; } /* * Add a slot to a transmitter's Python signal list. The signal is a Python * signal, the slot may be either a Qt signal, a Qt slot, a Python signal or a * Python slot. */ static int addSlotToPySigList(sipWrapper *txSelf,const char *sig, PyObject *rxObj,const char *slot) { sipPySig *ps; sipSlotList *psrx; /* Create a new one if necessary. */ if ((ps = findPySignal(txSelf,sig)) == NULL) { if ((ps = (sipPySig *)sip_api_malloc(sizeof (sipPySig))) == NULL) return -1; if ((ps -> name = sipStrdup(sig)) == NULL) { sip_api_free(ps); return -1; } ps -> rxlist = NULL; ps -> next = txSelf -> pySigList; txSelf -> pySigList = ps; } /* Create the new receiver. */ if ((psrx = (sipSlotList *)sip_api_malloc(sizeof (sipSlotList))) == NULL) return -1; if (saveSlot(&psrx->rx, rxObj, slot) < 0) { sip_api_free(psrx); return -1; } psrx -> next = ps -> rxlist; ps -> rxlist = psrx; return 0; } /* * Compare two slots to see if they are the same. */ static int isSameSlot(sipSlot *slot1,PyObject *rxobj2,const char *slot2) { /* See if they are signals or Qt slots, ie. they have a name. */ if (slot1 -> name != NULL) return (slot2 != NULL && sipQtSupport->qt_same_name(slot1 -> name,slot2) && slot1 -> pyobj == rxobj2); /* Both must be Python slots. */ if (slot2 != NULL) return 0; /* See if they are Python methods. */ if (slot1 -> pyobj == NULL) return (PyMethod_Check(rxobj2) && slot1 -> meth.mfunc == PyMethod_GET_FUNCTION(rxobj2) && slot1 -> meth.mself == PyMethod_GET_SELF(rxobj2) && slot1 -> meth.mclass == PyMethod_GET_CLASS(rxobj2)); if (PyMethod_Check(rxobj2)) return 0; /* The objects must be the same. */ return (slot1 -> pyobj == rxobj2); } /* * Convert a valid Python signal or slot to an existing universal slot. */ void *sipGetRx(sipWrapper *txSelf,const char *sigargs,PyObject *rxObj, const char *slot,const char **memberp) { if (slot != NULL) if (isQtSlot(slot) || isQtSignal(slot)) { void *rx; *memberp = slot; if ((rx = sip_api_get_cpp_ptr((sipWrapper *)rxObj, sipQObjectClass)) == NULL) return NULL; if (isQtSignal(slot)) rx = findSignal(rx, memberp); return rx; } /* * The slot was either a Python callable or PyQt3 Python signal so there * should be a universal slot. */ return sipQtSupport->qt_find_slot(sipGetAddress(txSelf), sigargs, rxObj, slot, memberp); } /* * Convert a Python receiver (either a Python signal or slot or a Qt signal or * slot) to a Qt receiver. It is only ever called when the signal is a Qt * signal. Return NULL is there was an error. */ void *sip_api_convert_rx(sipWrapper *txSelf,const char *sig,PyObject *rxObj, const char *slot,const char **memberp) { if (slot == NULL) return createUniversalSlot(txSelf, sig, rxObj, NULL, memberp); if (isQtSlot(slot) || isQtSignal(slot)) { void *rx; *memberp = slot; if ((rx = sip_api_get_cpp_ptr((sipWrapper *)rxObj, sipQObjectClass)) == NULL) return NULL; if (isQtSignal(slot)) rx = newSignal(rx, memberp); return rx; } /* The slot is a Python signal so we need a universal slot to catch it. */ return createUniversalSlot(txSelf, sig, rxObj, slot, memberp); } /* * Connect a Qt signal or a Python signal to a Qt slot, a Qt signal, a Python * slot or a Python signal. This is all possible combinations. */ PyObject *sip_api_connect_rx(PyObject *txObj,const char *sig,PyObject *rxObj, const char *slot, int type) { sipWrapper *txSelf = (sipWrapper *)txObj; /* Handle Qt signals. */ if (isQtSignal(sig)) { void *tx, *rx; const char *member, *real_sig; int res; if ((tx = sip_api_get_cpp_ptr(txSelf, sipQObjectClass)) == NULL) return NULL; real_sig = sig; if ((tx = newSignal(tx, &real_sig)) == NULL) return NULL; if ((rx = sip_api_convert_rx(txSelf, sig, rxObj, slot, &member)) == NULL) return NULL; res = sipQtSupport->qt_connect(tx, real_sig, rx, member, type); return PyBool_FromLong(res); } /* Handle Python signals. */ if (addSlotToPySigList(txSelf, sig, rxObj, slot) < 0) return NULL; Py_INCREF(Py_True); return Py_True; } /* * Disconnect a signal to a signal or a Qt slot. */ PyObject *sip_api_disconnect_rx(PyObject *txObj,const char *sig, PyObject *rxObj,const char *slot) { sipWrapper *txSelf = (sipWrapper *)txObj; /* Handle Qt signals. */ if (isQtSignal(sig)) { void *tx, *rx; const char *member; int res; if ((tx = sip_api_get_cpp_ptr(txSelf, sipQObjectClass)) == NULL) return NULL; if ((rx = sipGetRx(txSelf, sig, rxObj, slot, &member)) == NULL) { Py_INCREF(Py_False); return Py_False; } /* Handle Python signals. */ tx = findSignal(tx, &sig); res = sipQtSupport->qt_disconnect(tx, sig, rx, member); /* * Delete it if it is a universal slot as this will be it's only * connection. If the slot is actually a universal signal then it * should leave it in place. */ sipQtSupport->qt_destroy_universal_slot(rx); return PyBool_FromLong(res); } /* Handle Python signals. */ removeSlotFromPySigList(txSelf,sig,rxObj,slot); Py_INCREF(Py_True); return Py_True; } /* * Remove a slot from a transmitter's Python signal list. */ static void removeSlotFromPySigList(sipWrapper *txSelf,const char *sig, PyObject *rxObj,const char *slot) { sipPySig *ps; if ((ps = findPySignal(txSelf,sig)) != NULL) { sipSlotList **psrxp; for (psrxp = &ps -> rxlist; *psrxp != NULL; psrxp = &(*psrxp) -> next) { sipSlotList *psrx = *psrxp; if (isSameSlot(&psrx -> rx,rxObj,slot)) { *psrxp = psrx -> next; sipFreeSlotList(psrx); break; } } } } /* * Free a sipSlot structure. */ static void freeSlot(sipSlot *slot) { if (slot->name != NULL) sip_api_free(slot->name); else { PyObject *lam = slot->pyobj; if (lam != NULL && (lam == Py_None || sipLambdaSlot(lam))) Py_DECREF(lam); } /* Remove any weak reference. */ Py_XDECREF(slot->weakSlot); } /* * Free a sipSlotList structure on the heap. */ void sipFreeSlotList(sipSlotList *rx) { freeSlot(&rx->rx); sip_api_free(rx); } /* * Implement strdup() using sip_api_malloc(). */ static char *sipStrdup(const char *s) { char *d; if ((d = (char *)sip_api_malloc(strlen(s) + 1)) != NULL) strcpy(d,s); return d; } /* * Initialise a slot, returning 0 if there was no error. If the signal was a * Qt signal, then the slot may be a Python signal or a Python slot. If the * signal was a Python signal, then the slot may be anything. */ static int saveSlot(sipSlot *sp, PyObject *rxObj, const char *slot) { sp -> weakSlot = NULL; if (slot == NULL) { sp -> name = NULL; if (PyMethod_Check(rxObj)) { /* * Python creates methods on the fly. We could increment the * reference count to keep it alive, but that would keep "self" * alive as well and would probably be a circular reference. * Instead we remember the component parts and hope they are still * valid when we re-create the method when we need it. */ sipSaveMethod(&sp -> meth,rxObj); /* Notice if the class instance disappears. */ sp -> weakSlot = getWeakRef(sp -> meth.mself); /* This acts a flag to say that the slot is a method. */ sp -> pyobj = NULL; } else { PyObject *self; /* * We know that it is another type of callable, ie. a * function/builtin. */ if (PyCFunction_Check(rxObj) && (self = PyCFunction_GET_SELF(rxObj)) != NULL && sip_api_wrapper_check(self)) { /* * It is a wrapped C++ class method. We can't keep a copy * because they are generated on the fly and we can't take a * reference as that may keep the instance (ie. self) alive. * We therefore treat it as if the user had specified the slot * at "obj, SLOT('meth()')" rather than "obj.meth" (see below). */ const char *meth; /* Get the method name. */ meth = ((PyCFunctionObject *)rxObj) -> m_ml -> ml_name; if ((sp -> name = (char *)sip_api_malloc(strlen(meth) + 2)) == NULL) return -1; /* * Copy the name and set the marker that it needs converting to * a built-in method. */ sp -> name[0] = '\0'; strcpy(&sp -> name[1],meth); sp -> pyobj = self; sp -> weakSlot = getWeakRef(self); } else { /* * A bit of a hack to allow lamba functions to be used as * slots. */ if (sipLambdaSlot(rxObj)) Py_INCREF(rxObj); /* * It's unlikely that we will succeed in getting a weak * reference to the slot, but there is no harm in trying (and * future versions of Python may support references to more * object types). */ sp -> pyobj = rxObj; sp -> weakSlot = getWeakRef(rxObj); } } } else if ((sp -> name = sipStrdup(slot)) == NULL) return -1; else if (isQtSlot(slot)) { /* * The user has decided to connect a Python signal to a Qt slot and * specified the slot as "obj, SLOT('meth()')" rather than "obj.meth". */ char *tail; /* Remove any arguments. */ if ((tail = strchr(sp -> name,'(')) != NULL) *tail = '\0'; /* * A bit of a hack to indicate that this needs converting to a built-in * method. */ sp -> name[0] = '\0'; /* Notice if the class instance disappears. */ sp -> weakSlot = getWeakRef(rxObj); sp -> pyobj = rxObj; } else /* It's a Qt signal. */ sp -> pyobj = rxObj; return 0; } /* * Return a weak reference to the given object. */ static PyObject *getWeakRef(PyObject *obj) { PyObject *wr; if ((wr = PyWeakref_NewRef(obj,NULL)) == NULL) PyErr_Clear(); return wr; } /* * See if an object is a lambda function. */ int sipLambdaSlot(PyObject *slotObj) { if (!PyFunction_Check(slotObj)) return FALSE; return (strcmp(PyString_AsString(((PyFunctionObject *)slotObj)->func_name), "") == 0); }