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 /pysignals.c | |
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
Diffstat (limited to 'pysignals.c')
-rw-r--r-- | pysignals.c | 463 |
1 files changed, 404 insertions, 59 deletions
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; } |