diff options
author | Christopher Davis <loafier@gmail.com> | 2006-06-23 03:51:52 +0000 |
---|---|---|
committer | Christopher Davis <loafier@gmail.com> | 2006-06-23 03:51:52 +0000 |
commit | 4d33c04f15e60e21a537edd635c9ac130312a3cb (patch) | |
tree | 9530c793afd67ef5c8319c452d33011c7f7fe197 | |
parent | c76f11d4ead827d8e87e265131a3dee534bc0792 (diff) | |
download | irssi-python-4d33c04f15e60e21a537edd635c9ac130312a3cb.tar.gz irssi-python-4d33c04f15e60e21a537edd635c9ac130312a3cb.tar.xz irssi-python-4d33c04f15e60e21a537edd635c9ac130312a3cb.zip |
Began work on signal handler/map system. It seems to be working;
however, events with varying <cmd> text aren't handled because
it does a straight lookup in the hashtable with the signal name
to locate the argument list. Will need to change the system to
accomodate the <cmd> events.
The basics for reference arg support is there but not well tested.
git-svn-id: http://svn.irssi.org/repos/irssi-python@4289 dbcabf3a-b0e7-0310-adc4-f8d773084564
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | classes.txt | 2 | ||||
-rw-r--r-- | objects/pyscript-object.c | 63 | ||||
-rw-r--r-- | objects/pyscript-object.h | 1 | ||||
-rw-r--r-- | pycore.c | 4 | ||||
-rw-r--r-- | pyloader.c | 18 | ||||
-rw-r--r-- | pymodule.c | 75 | ||||
-rw-r--r-- | pysigmap.h | 177 | ||||
-rw-r--r-- | pysignals.c | 463 | ||||
-rw-r--r-- | pysignals.h | 23 | ||||
-rw-r--r-- | sig2code.py | 124 |
11 files changed, 866 insertions, 87 deletions
@@ -22,6 +22,9 @@ pyobjects.a: %.o: %.c $(CC) -c $< $(CFLAGS) +signalmap: + python sig2code.py < ~/irssi-0.8.10/docs/signals.txt > pysigmap.h + clean: rm -f *.o *.so cd objects/ && make clean diff --git a/classes.txt b/classes.txt index e94fd97..169d1d7 100644 --- a/classes.txt +++ b/classes.txt @@ -3,8 +3,6 @@ Basic types (source): IrssiChatObject/ Chatnet/ (core/chatnets.h) IrcChatnet (irc/core/irc-chatnets.h) - #ServerSetup/ (core/servers-setup.h) - # IrcServerSetup (irc/core/irc-servers-setup.h) ServerConnect/ (core/servers.h) IrcServerConnect (irc/core/irc-servers.h) Server/ (core/servers.h) diff --git a/objects/pyscript-object.c b/objects/pyscript-object.c index f74861f..d4ec5f1 100644 --- a/objects/pyscript-object.c +++ b/objects/pyscript-object.c @@ -65,24 +65,60 @@ static PyObject *PyScript_new(PyTypeObject *type, PyObject *args, PyObject *kwds return (PyObject *)self; } +//FIXME: add prioriety as arg +PyDoc_STRVAR(PyScript_command_bind_doc, + "Bind a command" +); static PyObject *PyScript_command_bind(PyScript *self, PyObject *args, PyObject *kwds) { - static char *kwlist[] = {"cmd", "func", "category", NULL}; + static char *kwlist[] = {"cmd", "func", "category", "priority", NULL}; char *cmd; PyObject *func; char *category = NULL; + int priority = SIGNAL_PRIORITY_DEFAULT; PY_SIGNAL_REC *srec; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "sO|s", kwlist, &cmd, &func, &category)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "sO|si", kwlist, + &cmd, &func, &category, &priority)) return NULL; if (!PyCallable_Check(func)) return PyErr_Format(PyExc_TypeError, "func must be callable"); - srec = py_command_bind(cmd, func, category); - + srec = pysignals_command_bind(cmd, func, category, priority); + if (!srec) + return PyErr_Format(PyExc_RuntimeError, "unable to bind command"); + /* add record to internal list*/ - self->signals = g_slist_append(self->signals, crec); + self->signals = g_slist_append(self->signals, srec); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyScript_signal_add_doc, + "add signal" +); +static PyObject *PyScript_signal_add(PyScript *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"signal", "func", "category", "priority", NULL}; + char *signal; + PyObject *func; + char *category = NULL; + int priority = SIGNAL_PRIORITY_DEFAULT; + PY_SIGNAL_REC *srec; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "sO|si", kwlist, + &signal, &func, &category, &priority)) + return NULL; + + if (!PyCallable_Check(func)) + return PyErr_Format(PyExc_TypeError, "func must be callable"); + + srec = pysignals_signal_add(signal, func, priority); + if (!srec) + return PyErr_Format(PyExc_KeyError, "unable to find signal, '%s'", signal); + + self->signals = g_slist_append(self->signals, srec); Py_RETURN_NONE; } @@ -96,8 +132,10 @@ static PyMemberDef PyScript_members[] = { /* Methods for object */ static PyMethodDef PyScript_methods[] = { - {"command_bind", (PyCFunction)PyScript_command_bind, - METH_VARARGS | METH_KEYWORDS, "Bind a command"}, + {"command_bind", (PyCFunction)PyScript_command_bind, METH_VARARGS | METH_KEYWORDS, + PyScript_command_bind_doc}, + {"signal_add", (PyCFunction)PyScript_signal_add, METH_VARARGS | METH_KEYWORDS, + PyScript_signal_add_doc}, {NULL} /* Sentinel */ }; @@ -187,15 +225,8 @@ void pyscript_remove_signals(PyObject *script) self = (PyScript *) script; for (node = self->signals; node != NULL; node = node->next) - { - PY_SIGNAL_REC *crec = node->data; - - py_command_unbind(crec); - g_free(crec->name); - Py_DECREF(crec->handler); - g_free(crec); - } - + pysignals_remove_generic((PY_SIGNAL_REC *)node->data); + g_slist_free(self->signals); self->signals = NULL; } diff --git a/objects/pyscript-object.h b/objects/pyscript-object.h index 260c6da..ee32d8c 100644 --- a/objects/pyscript-object.h +++ b/objects/pyscript-object.h @@ -3,6 +3,7 @@ #include <Python.h> #include <glib.h> +//FIXME: add list of registered dynamic signal names typedef struct { PyObject_HEAD PyObject *module; /* module object */ @@ -6,9 +6,9 @@ #include "pycore.h" #include "pyloader.h" #include "pymodule.h" +#include "pysignals.h" #include "factory.h" - /*XXX: copy parse into utils */ static void cmd_exec(const char *data) { @@ -117,6 +117,7 @@ void irssi_python_init(void) { Py_InitializeEx(0); + pysignals_init(); if (!pyloader_init() || !pymodule_init() || !factory_init()) { printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Failed to load Python"); @@ -148,5 +149,6 @@ void irssi_python_deinit(void) pymodule_deinit(); pyloader_deinit(); + pysignals_deinit(); Py_Finalize(); } @@ -288,6 +288,20 @@ int pyloader_init(void) return 1; } +static void py_clear_scripts() +{ + int i; + + for (i = 0; i < PyList_Size(script_modules); i++) + { + PyObject *scr = PyList_GET_ITEM(script_modules, i); + pyscript_remove_signals(scr); + pyscript_clear_modules(scr); + } + + Py_DECREF(script_modules); +} + void pyloader_deinit(void) { GSList *node; @@ -300,7 +314,5 @@ void pyloader_deinit(void) g_slist_free(script_paths); script_paths = NULL; - /* script specific resources (cmd + signal handlers) should be removed - by the object's destructor */ - Py_DECREF(script_modules); + py_clear_scripts(); } @@ -773,11 +773,75 @@ static PyObject *py_notifylist_find(PyObject *self, PyObject *args, PyObject *kw rec = notifylist_find(mask, ircnet); if (rec) - pynotifylist_new(rec); + return pynotifylist_new(rec); Py_RETURN_NONE; } +PyDoc_STRVAR(py_commands_doc, + "Return a list of all commands." +); +static PyObject *py_commands(PyObject *self, PyObject *args) +{ + return py_irssi_objlist_new(commands, 1, (InitFunc)pycommand_new); +} + +PyDoc_STRVAR(py_level2bits_doc, + "Level string -> number" +); +static PyObject *py_level2bits(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"level", NULL}; + char *level = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &level)) + return NULL; + + return PyLong_FromUnsignedLong(level2bits(level)); +} + +PyDoc_STRVAR(py_bits2level_doc, + "Level number -> string" +); +static PyObject *py_bits2level(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"bits", NULL}; + unsigned bits; + char *str; + PyObject *ret; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "I", kwlist, + &bits)) + return NULL; + + str = bits2level(bits); + if (str) + { + ret = PyString_FromString(str); + g_free(str); + return ret; + } + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(py_combine_level_doc, + "Combine level number to level string ('+level -level'). Return new level number." +); +static PyObject *py_combine_level(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"level", "str", NULL}; + int level = 0; + char *str = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "is", kwlist, + &level, &str)) + return NULL; + + return PyLong_FromUnsignedLong(combine_level(level, str)); +} + static PyMethodDef ModuleMethods[] = { {"prnt", (PyCFunction)py_prnt, METH_VARARGS|METH_KEYWORDS, py_prnt_doc}, {"get_script", (PyCFunction)py_get_script, METH_NOARGS, py_get_script_doc}, @@ -873,9 +937,18 @@ static PyMethodDef ModuleMethods[] = { py_notifylist_ison_doc}, {"notifylist_find", (PyCFunction)py_notifylist_find, METH_VARARGS | METH_KEYWORDS, py_notifylist_find_doc}, + {"commands", (PyCFunction)py_commands, METH_NOARGS, + py_commands_doc}, + {"level2bits", (PyCFunction)py_level2bits, METH_VARARGS | METH_KEYWORDS, + py_level2bits_doc}, + {"bits2level", (PyCFunction)py_bits2level, METH_VARARGS | METH_KEYWORDS, + py_bits2level_doc}, + {"combine_level", (PyCFunction)py_combine_level, METH_VARARGS | METH_KEYWORDS, + py_combine_level_doc}, {NULL, NULL, 0, NULL} /* Sentinel */ }; +/*XXX: move to pyloader.c??*/ /* Traverse stack backwards to find the nearest _script object in globals */ static PyObject *find_script(void) { diff --git a/pysigmap.h b/pysigmap.h new file mode 100644 index 0000000..0e37935 --- /dev/null +++ b/pysigmap.h @@ -0,0 +1,177 @@ +/* Include in your C module */ +static PY_SIGNAL_SPEC_REC py_sigmap[] = { + {"gui dialog", "ss", 0, 0, 0}, + {"send command", "sSW", 0, 0, 0}, + {"chat protocol created", "?", 0, 0, 0}, + {"chat protocol updated", "?", 0, 0, 0}, + {"chat protocol destroyed", "?", 0, 0, 0}, + {"channel created", "Ci", 0, 0, 0}, + {"channel destroyed", "C", 0, 0, 0}, + {"chatnet created", "c", 0, 0, 0}, + {"chatnet destroyed", "c", 0, 0, 0}, + {"commandlist new", "o", 0, 0, 0}, + {"commandlist remove", "o", 0, 0, 0}, + {"error command", "is", 0, 0, 0}, + {"send command", "sSW", 0, 0, 0}, + {"send text", "sSW", 0, 0, 0}, + {"default command", "sSW", 0, 0, 0}, + {"ignore created", "g", 0, 0, 0}, + {"ignore destroyed", "g", 0, 0, 0}, + {"ignore changed", "g", 0, 0, 0}, + {"log new", "l", 0, 0, 0}, + {"log remove", "l", 0, 0, 0}, + {"log create failed", "l", 0, 0, 0}, + {"log locked", "l", 0, 0, 0}, + {"log started", "l", 0, 0, 0}, + {"log stopped", "l", 0, 0, 0}, + {"log rotated", "l", 0, 0, 0}, + {"log written", "ls", 0, 0, 0}, + {"module loaded", "??", 0, 0, 0}, + {"module unloaded", "??", 0, 0, 0}, + {"module error", "isss", 0, 0, 0}, + {"nicklist new", "Cn", 0, 0, 0}, + {"nicklist remove", "Cn", 0, 0, 0}, + {"nicklist changed", "Cns", 0, 0, 0}, + {"nicklist host changed", "Cn", 0, 0, 0}, + {"nicklist gone changed", "Cn", 0, 0, 0}, + {"nicklist serverop changed", "Cn", 0, 0, 0}, + {"pidwait", "ii", 0, 0, 0}, + {"query created", "qi", 0, 0, 0}, + {"query destroyed", "q", 0, 0, 0}, + {"query nick changed", "qs", 0, 0, 0}, + {"window item name changed", "W", 0, 0, 0}, + {"query address changed", "q", 0, 0, 0}, + {"query server changed", "qS", 0, 0, 0}, + {"rawlog", "as", 0, 0, 0}, + {"server looking", "S", 0, 0, 0}, + {"server connected", "S", 0, 0, 0}, + {"server connecting", "Su", 0, 0, 0}, + {"server connect failed", "S", 0, 0, 0}, + {"server disconnected", "S", 0, 0, 0}, + {"server quit", "Ss", 0, 0, 0}, + {"server sendmsg", "Sssi", 0, 0, 0}, + {"setup reread", "s", 0, 0, 0}, + {"setup saved", "si", 0, 0, 0}, + {"ban type changed", "s", 0, 0, 0}, + {"channel joined", "C", 0, 0, 0}, + {"channel wholist", "C", 0, 0, 0}, + {"channel sync", "C", 0, 0, 0}, + {"channel topic changed", "C", 0, 0, 0}, + {"ctcp msg", "Sssss", 0, 0, 0}, + {"default ctcp msg", "Sssss", 0, 0, 0}, + {"ctcp reply", "Sssss", 0, 0, 0}, + {"default ctcp reply", "Sssss", 0, 0, 0}, + {"ctcp action", "Sssss", 0, 0, 0}, + {"awaylog show", "lii", 0, 0, 0}, + {"server nick changed", "S", 0, 0, 0}, + {"event connected", "S", 0, 0, 0}, + {"server event", "Ssss", 0, 0, 0}, + {"default event", "Ssss", 0, 0, 0}, + {"whois default event", "Ssss", 0, 0, 0}, + {"server incoming", "Ss", 0, 0, 0}, + {"server lag", "S", 0, 0, 0}, + {"server lag disconnect", "S", 0, 0, 0}, + {"massjoin", "CL", 0, 0, 0}, + {"ban new", "Cb", 0, 0, 0}, + {"ban remove", "Cbs", 0, 0, 0}, + {"channel mode changed", "Cs", 0, 0, 0}, + {"nick mode changed", "Cnsss", 0, 0, 0}, + {"user mode changed", "Ss", 0, 0, 0}, + {"away mode changed", "S", 0, 0, 0}, + {"netsplit server new", "Se", 0, 0, 0}, + {"netsplit server remove", "Se", 0, 0, 0}, + {"netsplit new", "N", 0, 0, 0}, + {"netsplit remove", "N", 0, 0, 0}, + {"default dcc ctcp", "sd", 0, 0, 0}, + {"dcc unknown ctcp", "sss", 0, 0, 0}, + {"default dcc reply", "sd", 0, 0, 0}, + {"dcc unknown reply", "sss", 0, 0, 0}, + {"dcc chat message", "ds", 0, 0, 0}, + {"dcc created", "d", 0, 0, 0}, + {"dcc destroyed", "d", 0, 0, 0}, + {"dcc connected", "d", 0, 0, 0}, + {"dcc rejecting", "d", 0, 0, 0}, + {"dcc closed", "d", 0, 0, 0}, + {"dcc request", "ds", 0, 0, 0}, + {"dcc request send", "d", 0, 0, 0}, + {"dcc chat message", "ds", 0, 0, 0}, + {"dcc transfer update", "d", 0, 0, 0}, + {"dcc get receive", "d", 0, 0, 0}, + {"dcc error connect", "d", 0, 0, 0}, + {"dcc error file create", "ds", 0, 0, 0}, + {"dcc error file open", "ssi", 0, 0, 0}, + {"dcc error get not found", "s", 0, 0, 0}, + {"dcc error send exists", "ss", 0, 0, 0}, + {"dcc error unknown type", "s", 0, 0, 0}, + {"dcc error close not found", "sss", 0, 0, 0}, + {"autoignore new", "S?", 0, 0, 0}, + {"autoignore remove", "S?", 0, 0, 0}, + {"flood", "Sssis", 0, 0, 0}, + {"notifylist new", "O", 0, 0, 0}, + {"notifylist remove", "O", 0, 0, 0}, + {"notifylist joined", "Ssssss", 0, 0, 0}, + {"notifylist away changed", "Ssssss", 0, 0, 0}, + {"notifylist unidle", "Ssssss", 0, 0, 0}, + {"notifylist left", "Ssssss", 0, 0, 0}, + {"proxy client connected", "?", 0, 0, 0}, + {"proxy client disconnected", "?", 0, 0, 0}, + {"gui print text", "wiiist", 0, 0, 0}, + {"gui print text finished", "w", 0, 0, 0}, + {"complete word", "GwssI", 0, 0, 0}, + {"exec new", "p", 0, 0, 0}, + {"exec remove", "pi", 0, 0, 0}, + {"exec input", "ps", 0, 0, 0}, + {"message public", "Sssss", 0, 0, 0}, + {"message private", "Ssss", 0, 0, 0}, + {"message own_public", "Sss", 0, 0, 0}, + {"message own_private", "Ssss", 0, 0, 0}, + {"message join", "Ssss", 0, 0, 0}, + {"message part", "Sssss", 0, 0, 0}, + {"message quit", "Ssss", 0, 0, 0}, + {"message kick", "Ssssss", 0, 0, 0}, + {"message nick", "Ssss", 0, 0, 0}, + {"message own_nick", "Ssss", 0, 0, 0}, + {"message invite", "Ssss", 0, 0, 0}, + {"message topic", "Sssss", 0, 0, 0}, + {"keyinfo created", "?", 0, 0, 0}, + {"keyinfo destroyed", "?", 0, 0, 0}, + {"print text", "tss", 0, 0, 0}, + {"theme created", "?", 0, 0, 0}, + {"theme destroyed", "?", 0, 0, 0}, + {"window hilight", "w", 0, 0, 0}, + {"window activity", "wi", 0, 0, 0}, + {"window item hilight", "W", 0, 0, 0}, + {"window item activity", "Wi", 0, 0, 0}, + {"window item new", "wW", 0, 0, 0}, + {"window item remove", "wW", 0, 0, 0}, + {"window item changed", "wW", 0, 0, 0}, + {"window item server changed", "wW", 0, 0, 0}, + {"window created", "w", 0, 0, 0}, + {"window destroyed", "w", 0, 0, 0}, + {"window changed", "ww", 0, 0, 0}, + {"window changed automatic", "w", 0, 0, 0}, + {"window server changed", "wS", 0, 0, 0}, + {"window refnum changed", "wi", 0, 0, 0}, + {"window name changed", "w", 0, 0, 0}, + {"window history changed", "ws", 0, 0, 0}, + {"window level changed", "w", 0, 0, 0}, + {"message irc op_public", "Sssss", 0, 0, 0}, + {"message irc own_wall", "Sss", 0, 0, 0}, + {"message irc own_action", "Sss", 0, 0, 0}, + {"message irc action", "Sssss", 0, 0, 0}, + {"message irc own_notice", "Sss", 0, 0, 0}, + {"message irc notice", "Sssss", 0, 0, 0}, + {"message irc own_ctcp", "Ssss", 0, 0, 0}, + {"message irc ctcp", "Ssssss", 0, 0, 0}, + {"message irc mode", "Sssss", 0, 0, 0}, + {"message dcc own", "ds", 0, 0, 0}, + {"message dcc own_action", "ds", 0, 0, 0}, + {"message dcc own_ctcp", "dss", 0, 0, 0}, + {"message dcc", "ds", 0, 0, 0}, + {"message dcc action", "ds", 0, 0, 0}, + {"message dcc ctcp", "dss", 0, 0, 0}, + {"gui key pressed", "i", 0, 0, 0}, + {NULL} +}; + +#define py_sigmap_len() (sizeof(py_sigmap) / sizeof(py_sigmap[0]) - 1) diff --git a/pysignals.c b/pysignals.c index c3764fb..fb4a3d9 100644 --- a/pysignals.c +++ b/pysignals.c @@ -3,93 +3,438 @@ #include "pysignals.h" #include "factory.h" -static void py_command_proxy(char *data, SERVER_REC *server, WI_ITEM_REC *witem); +/* NOTE: + * Signals must be registered to be accessible to Python scripts. + * + * Registering a dynamic signal adds a new SPEC_REC with a refcount of 1. + * If another script registers the same signal, refcount is incremented. + * Binding to the signal also increments its reference count. Likewise, + * unregistering or unbinding a dynamic signal will decrement its refcount. + * When refcount hits 0, the dynamic signal is removed from the list, and + * scripts can no longer bind to it. + */ -/* crec should be owned by a Script object */ -void py_command_bind(const char *category, PY_COMMAND_REC *crec) +//FIXME: does not handle binding of signals with varying <cmd> text + +typedef struct _PY_SIGNAL_SPEC_REC +{ + char *name; + char *arglist; + int id; + int refcount; + int dynamic; +} PY_SIGNAL_SPEC_REC; + +#include "pysigmap.h" + +static GHashTable *py_sighash = NULL; + +static void py_run_handler(PY_SIGNAL_REC *rec, void **args); +static void py_sig_proxy(void *p1, void *p2, void *p3, void *p4, void *p5, void *p6); +static void py_signal_ref(PY_SIGNAL_SPEC_REC *sig); +static int py_signal_unref(PY_SIGNAL_SPEC_REC *sig); +static void py_signal_add(PY_SIGNAL_SPEC_REC *sig); +static PY_SIGNAL_REC *py_signal_rec_new(const char *signal, PyObject *func, const char *command); +static void py_signal_rec_destroy(PY_SIGNAL_REC *sig); +static PyObject *py_mkstrlist(void *iobj); +static PyObject *py_i2py(char code, void *iobj); +static void py_getstrlist(GList **list, PyObject *pylist); + +PY_SIGNAL_REC *pysignals_command_bind(const char *cmd, PyObject *func, + const char *category, int priority) { - command_bind_full(MODULE_NAME, SIGNAL_PRIORITY_DEFAULT, crec->name, - -1, category, (SIGNAL_FUNC)py_command_proxy, crec); + PY_SIGNAL_REC *rec = py_signal_rec_new("send command", func, cmd); + g_return_val_if_fail(rec != NULL, NULL); + + command_bind_full(MODULE_NAME, priority, cmd, + -1, category, (SIGNAL_FUNC)py_sig_proxy, rec); + + return rec; } -void py_command_unbind(PY_COMMAND_REC *crec) +/* return NULL if signal is invalid */ +PY_SIGNAL_REC *pysignals_signal_add(const char *signal, PyObject *func, int priority) { - command_unbind_full(crec->name, (SIGNAL_FUNC)py_command_proxy, crec); + PY_SIGNAL_REC *rec = py_signal_rec_new(signal, func, NULL); + + if (rec == NULL) + return NULL; + + signal_add_full_id(MODULE_NAME, priority, rec->signal->id, + (SIGNAL_FUNC)py_sig_proxy, rec); + + return rec; } -/* This is just for testing. A complete version would use a signal map, a better - wrapper object factory system, and a generic signal handler like in the Perl - bindings */ -static void py_command_proxy(char *data, SERVER_REC *server, WI_ITEM_REC *witem) +void pysignals_command_unbind(PY_SIGNAL_REC *rec) { - PY_COMMAND_REC *crec; - PyObject *ret, *pyserver, *pywitem; + g_return_if_fail(rec->command != NULL); -#if 0 - if (server) - { - CHAT_PROTOCOL_REC *chat = chat_protocol_find_id(server->chat_type); - if (chat && !strcmp(chat->name, "IRC")) - pyserver = pyirc_server_new(server); - else - pyserver = pyserver_new(server); - g_assert(pyserver != NULL); - } + command_unbind_full(rec->command, (SIGNAL_FUNC)py_sig_proxy, rec); + py_signal_rec_destroy(rec); +} + +void pysignals_signal_remove(PY_SIGNAL_REC *rec) +{ + g_return_if_fail(rec->command == NULL); + + signal_remove_id(rec->signal->id, (SIGNAL_FUNC)py_sig_proxy, rec); + py_signal_rec_destroy(rec); +} + +void pysignals_remove_generic(PY_SIGNAL_REC *rec) +{ + if (rec->command) + pysignals_command_unbind(rec); else + pysignals_signal_remove(rec); +} + +static PyObject *py_mkstrlist(void *iobj) +{ + PyObject *list; + GList *node, **ptr; + ptr = iobj; + + list = PyList_New(0); + if (!list) + return NULL; + + for (node = *ptr; node != NULL; node = node->next) { - pyserver = Py_None; - Py_INCREF(pyserver); + int ret; + PyObject *str; + + str = PyString_FromString(node->data); + if (!str) + { + Py_DECREF(list); + return NULL; + } + + ret = PyList_Append(list, str); + Py_DECREF(str); + if (ret != 0) + { + Py_DECREF(list); + return NULL; + } } - if (witem) + + return list; +} + +/* irssi obj -> PyObject */ +static PyObject *py_i2py(char code, void *iobj) +{ + if (iobj == NULL) + Py_RETURN_NONE; + + switch (code) { - char *type = module_find_id_str("WINDOW ITEM TYPE", witem->type); - g_assert(type != NULL); + case '?': + Py_RETURN_NONE; + + case 's': + return PyString_FromString((char *)iobj); + case 'u': + return PyLong_FromUnsignedLong(*(unsigned long*)iobj); + case 'I': + return PyInt_FromLong(*(int *)iobj); + case 'i': + return PyInt_FromLong((int)iobj); + + case 'G': + return py_mkstrlist(iobj); + case 'L': /* list of nicks */ + return py_irssi_chatlist_new((GSList *)iobj, 1); - if (!strcmp("CHANNEL", type)) - pywitem = pychannel_new(witem); - else if (!strcmp("QUERY", type)) - pywitem = pyquery_new(witem); - else - pywitem = pywindow_item_new(witem); + case 'c': + case 'S': + case 'C': + case 'q': + case 'n': + case 'W': + return py_irssi_chat_new(iobj, 1); - g_assert(pywitem != NULL); + case 'd': + return py_irssi_new(iobj, 1); + + case 'r': + return pyreconnect_new(iobj); + case 'o': + return pycommand_new(iobj); + case 'l': + return pylog_new(iobj); + case 'a': + return pyrawlog_new(iobj); + case 'g': + return pyignore_new(iobj); + case 'b': + return pyban_new(iobj); + case 'N': + return pynetsplit_new(iobj); + case 'e': + return pynetsplit_server_new(iobj); + case 'O': + return pynotifylist_new(iobj); + case 'p': + return pyprocess_new(iobj); + case 't': + return pytextdest_new(iobj); + case 'w': + return pywindow_new(iobj); } - else + + return PyErr_Format(PyExc_TypeError, "unknown code %c", code); +} + +static void py_getstrlist(GList **list, PyObject *pylist) +{ + GList *out = NULL; + int i; + PyObject *str; + char *cstr; + + for (i = 0; i < PyList_Size(pylist); i++) { - pywitem = Py_None; - Py_INCREF(pywitem); + str = PyList_GET_ITEM(pylist, i); + if (!PyString_Check(str)) + { + PyErr_SetString(PyExc_TypeError, "string list contains invalid elements"); + PyErr_Print(); + return; + } + + cstr = g_strdup(PyString_AS_STRING(str)); + out = g_list_append(out, cstr); } -#endif - if (server) + g_list_foreach(*list, (GFunc)g_free, NULL); + g_list_free(*list); + *list = out; +} + +static void py_run_handler(PY_SIGNAL_REC *rec, void **args) +{ + PyObject *argtup, *ret; + char *arglist = rec->signal->arglist; + int arglen, i, j; + + arglen = strlen(arglist); + g_return_if_fail(arglen <= SIGNAL_MAX_ARGUMENTS); + + argtup = PyTuple_New(arglen); + if (!argtup) + goto error; + + for (i = 0; i < arglen; i++) { - pyserver = py_irssi_chat_new(server, 1); - g_assert(pyserver != NULL); + PyObject *arg = py_i2py(arglist[i], args[i]); + if (!arg) + goto error; + + PyTuple_SET_ITEM(argtup, i, arg); } - else + + ret = PyObject_CallObject(rec->handler, argtup); + if (!ret) + goto error; + + /*XXX: reference arg handling not well tested */ + for (i = 0, j = 0; i < arglen; i++) { - pyserver = Py_None; - Py_INCREF(Py_None); + GList **list; + PyObject *pyarg = PyTuple_GET_ITEM(argtup, i); + + switch (arglist[i]) + { + case 'G': + list = args[i]; + py_getstrlist(list, pyarg); + break; + + case 'I': + if (ret != Py_None) + { + PyObject *value; + int *intarg = args[i]; + + /* expect a proper return value to set reference arg. + * if return is a tuple, find the next item + */ + if (PyTuple_Check(ret)) + value = PyTuple_GET_ITEM(ret, j++); + else + value = ret; + + if (!PyInt_Check(value)) + continue; + + *intarg = PyInt_AS_LONG(value); + } + break; + } } + + Py_XDECREF(ret); + +error: + Py_XDECREF(argtup); + if (PyErr_Occurred()) + PyErr_Print(); +} - if (witem) +static void py_sig_proxy(void *p1, void *p2, void *p3, void *p4, void *p5, void *p6) +{ + PY_SIGNAL_REC *rec = signal_get_user_data(); + void *args[6]; + + args[0] = p1; args[1] = p2; args[2] = p3; + args[3] = p4; args[4] = p5; args[5] = p6; + py_run_handler(rec, args); +} + +/* returns NULL if signal is invalid, incr reference to func */ +static PY_SIGNAL_REC *py_signal_rec_new(const char *signal, PyObject *func, const char *command) +{ + PY_SIGNAL_REC *rec; + PY_SIGNAL_SPEC_REC *spec; + + g_return_val_if_fail(func != NULL, NULL); + + spec = g_hash_table_lookup(py_sighash, signal); + if (!spec) + return NULL; + + rec = g_new(PY_SIGNAL_REC, 1); + rec->command = g_strdup(command); + rec->signal = spec; + rec->handler = func; + Py_INCREF(func); + + py_signal_ref(spec); + + return rec; +} + +static void py_signal_rec_destroy(PY_SIGNAL_REC *sig) +{ + py_signal_unref(sig->signal); + + Py_DECREF(sig->handler); + g_free(sig->command); + g_free(sig); +} + +static void py_signal_add(PY_SIGNAL_SPEC_REC *sig) +{ + g_hash_table_insert(py_sighash, sig->name, sig); +} + +static void py_signal_ref(PY_SIGNAL_SPEC_REC *sig) +{ + g_return_if_fail(sig->refcount >= 0); + + sig->refcount++; +} + +static int py_signal_unref(PY_SIGNAL_SPEC_REC *sig) +{ + g_return_val_if_fail(sig->refcount >= 1, 0); + + sig->refcount--; + + if (sig->refcount == 0) { - pywitem = py_irssi_chat_new(witem, 1); - g_assert(pywitem != NULL); + if (sig->dynamic) + { + g_hash_table_remove(py_sighash, sig->name); + + /* freeing name also takes care of the key */ + g_free(sig->name); + g_free(sig->arglist); + g_free(sig); + } + + /* non-dynamic signals are not removed */ + + return 1; } - else + + return 0; +} + +/* returns 0 when signal already exists, but with different args */ +int pysignals_register(const char *name, const char *arglist) +{ + PY_SIGNAL_SPEC_REC *spec; + + spec = g_hash_table_lookup(py_sighash, name); + if (!spec) { - pywitem = Py_None; - Py_INCREF(Py_None); + spec = g_new0(PY_SIGNAL_SPEC_REC, 1); + spec->dynamic = 1; + spec->refcount = 0; + spec->name = g_strdup(name); + spec->arglist = g_strdup(arglist); + spec->id = signal_get_uniq_id(name); + + g_hash_table_insert(py_sighash, spec->name, spec); } + else if (strcmp(spec->arglist, arglist) != 0) + return 0; + + spec->refcount++; + + return 1; +} + +/* returns 0 when name doesn't exist */ +int pysignals_unregister(const char *name) +{ + PY_SIGNAL_SPEC_REC *spec; + + spec = g_hash_table_lookup(py_sighash, name); + if (!spec) + return 0; + + py_signal_unref(spec); + return 1; +} + +void pysignals_init(void) +{ + int i; - crec = signal_get_user_data(); - ret = PyObject_CallFunction(crec->handler, "(sOO)", data, pyserver, pywitem); - if (!ret) - PyErr_Print(); - else - Py_DECREF(ret); + g_return_if_fail(py_sighash == NULL); + + py_sighash = g_hash_table_new((GHashFunc)g_str_hash, (GCompareFunc)g_str_equal); + + for (i = 0; i < py_sigmap_len(); i++) + { + py_sigmap[i].refcount = 1; + py_sigmap[i].dynamic = 0; + py_sigmap[i].id = signal_get_uniq_id(py_sigmap[i].name); + py_signal_add(&py_sigmap[i]); + } +} + +static int py_check_sig(char *key, PY_SIGNAL_SPEC_REC *value, void *data) +{ + /* shouldn't need to deallocate any script recs -- all remaining at + this point should not be dynamic. non dynamic signals should have + no outstanding references */ + g_return_val_if_fail(value->dynamic == 0, TRUE); + g_return_val_if_fail(value->refcount == 1, TRUE); - Py_DECREF(pyserver); - Py_DECREF(pywitem); + return TRUE; +} + +void pysignals_deinit(void) +{ + g_return_if_fail(py_sighash != NULL); + g_hash_table_foreach_remove(py_sighash, (GHRFunc)py_check_sig, NULL); + g_hash_table_destroy(py_sighash); + py_sighash = NULL; } diff --git a/pysignals.h b/pysignals.h index 823472e..f9ace24 100644 --- a/pysignals.h +++ b/pysignals.h @@ -2,13 +2,26 @@ #define _PYSIGNALS_H_ #include <Python.h> -typedef struct +/* forward */ +struct _PY_SIGNAL_SPEC_REC; + +typedef struct _PY_SIGNAL_REC { - char *name; + struct _PY_SIGNAL_SPEC_REC *signal; + char *command; /* NULL if this is signal */ PyObject *handler; -} PY_COMMAND_REC; +} PY_SIGNAL_REC; -void py_command_bind(const char *category, PY_COMMAND_REC *crec); -void py_command_unbind(PY_COMMAND_REC *crec); +PY_SIGNAL_REC *pysignals_command_bind(const char *cmd, PyObject *func, + const char *category, int priority); +PY_SIGNAL_REC *pysignals_signal_add(const char *signal, PyObject *func, + int priority); +void pysignals_command_unbind(PY_SIGNAL_REC *rec); +void pysignals_signal_remove(PY_SIGNAL_REC *rec); +void pysignals_remove_generic(PY_SIGNAL_REC *rec); +int pysignals_register(const char *name, const char *arglist); +int pysignals_unregister(const char *name); +void pysignals_init(void); +void pysignals_deinit(void); #endif diff --git a/sig2code.py b/sig2code.py new file mode 100644 index 0000000..48cee71 --- /dev/null +++ b/sig2code.py @@ -0,0 +1,124 @@ +import re +import sys + +code_text = """ + Conversion codes notes + I = int *arg IN/OUT + G = string GList **arg IN/OUT (list must be reconstructed) + L = list of nicks + + Scalars + s -> char * + u -> ulong * + I -> int * + i -> int + + Lists of things (completion.c and massjoin.c) + G -> GList * of char* + L -> GSList of NICK_RECs + + Chat objects + c -> CHATNET_REC + S -> SERVER_REC + C -> CHANNEL_REC + q -> QUERY_REC + n -> NICK_REC + W -> WI_ITEM_REC + + Irssi objects + d -> DCC_REC + + Other objects + r -> RECONNECT_REC + o -> COMMAND_REC + l -> LOG_REC + a -> RAWLOG_REC + g -> IGNORE_REC + ? -> MODULE_REC + b -> BAN_REC + N -> NETSPLIT_REC + e -> NETSPLIT_SERVER_REC + ? -> AUTOIGNORE_REC + O -> NOTIFYLIST_REC + ? -> THEME_REC + ? -> KEYINFO_REC + p -> PROCESS_REC + t -> TEXT_DEST_REC + w -> WINDOW_REC +""" + +#generate body of transcode function from the list of codes above +def prepair(): + lines = code_text.split('\n') + codes = [] + for ln in lines: + m = re.match('^.*([a-zA-Z\?]) -> (.*)$', ln) + if not m: continue + code, match = m.groups() + code = code.strip() + match = match.strip() + + assert code == '?' or code not in codes, "dupe code, " + code + codes.append(code) + + print "if arg.startswith('%s'): return '%s'" % (match, code) + +def transcode(arg): + if arg.startswith('char *'): return 's' + if arg.startswith('ulong *'): return 'u' + if arg.startswith('int *'): return 'I' + if arg.startswith('int'): return 'i' + if arg.startswith('GList * of char*'): return 'G' + if arg.startswith('GSList of NICK_RECs'): return 'L' + if arg.startswith('CHATNET_REC'): return 'c' + if arg.startswith('SERVER_REC'): return 'S' + if arg.startswith('RECONNECT_REC'): return 'r' + if arg.startswith('CHANNEL_REC'): return 'C' + if arg.startswith('QUERY_REC'): return 'q' + if arg.startswith('COMMAND_REC'): return 'o' + if arg.startswith('NICK_REC'): return 'n' + if arg.startswith('LOG_REC'): return 'l' + if arg.startswith('RAWLOG_REC'): return 'a' + if arg.startswith('IGNORE_REC'): return 'g' + if arg.startswith('MODULE_REC'): return '?' + if arg.startswith('BAN_REC'): return 'b' + if arg.startswith('NETSPLIT_REC'): return 'N' + if arg.startswith('NETSPLIT_SERVER_REC'): return 'e' + if arg.startswith('DCC_REC'): return 'd' + if arg.startswith('AUTOIGNORE_REC'): return '?' + if arg.startswith('NOTIFYLIST_REC'): return 'O' + if arg.startswith('THEME_REC'): return '?' + if arg.startswith('KEYINFO_REC'): return '?' + if arg.startswith('PROCESS_REC'): return 'p' + if arg.startswith('TEXT_DEST_REC'): return 't' + if arg.startswith('WINDOW_REC'): return 'w' + if arg.startswith('WI_ITEM_REC'): return 'W' + return '?' + +def main(): + + print "/* Include in your C module */" + print "static PY_SIGNAL_SPEC_REC py_sigmap[] = {" + + for ln in sys.stdin: + ln = ln.strip() + m = re.match('^\"([^\"]+)\",(.*)$', ln) + if not m: continue + + signal, args = m.groups() + + if signal.startswith('script '): continue + + argv = [transcode(a.strip()) for a in args.split(',')] + argv = ''.join(argv) + + print ' {"%s", "%s", 0, 0, 0},' % (signal, argv) + + print " {NULL}" + print "};" + print + print "#define py_sigmap_len() (sizeof(py_sigmap) / sizeof(py_sigmap[0]) - 1)" + +if __name__ == '__main__': + main() + |