summaryrefslogtreecommitdiffstats
path: root/pysignals.c
diff options
context:
space:
mode:
Diffstat (limited to 'pysignals.c')
-rw-r--r--pysignals.c121
1 files changed, 104 insertions, 17 deletions
diff --git a/pysignals.c b/pysignals.c
index daecea8..4e6d4ee 100644
--- a/pysignals.c
+++ b/pysignals.c
@@ -4,8 +4,32 @@
#include "factory.h"
/* NOTE:
- * Signals must be registered to be accessible to Python scripts.
+ * There are two different records used to store signal related data:
+ * PY_SIGNAL_SPEC_REC and PY_SIGNAL_REC. Each SPEC_REC declares a "plain"
+ * signal, or a type of "variable" signal.
+ *
+ * Plain signals are emitted by Irssi the same way every time, using the
+ * same text. They are never extended or altered in any way. Plain signals
+ * make up the vast majority of Irssi signals. These include (some random
+ * examples): "beep", "printtext", "log new", "ban new", etc.
+ *
+ * Variable signals are emitted by joining a common prefix with text that
+ * varies from emit to emit. These are signals that have a "<cmd>" suffix
+ * in Irssi's signal listing. irssi-python uses the trailing space to
+ * distinguish between plain signals and variable signals. Example prefixes:
+ * "redir ", "command ", "event ". "command nick" would be emitted when the
+ * user types /nick yournick at the prompt.
*
+ * While PY_SIGNAL_SPEC_REC stores data about each individual signal,
+ * PY_SIGNAL_REC stores data about each consumer of a signal (or command).
+ * A listing of SPEC_REC entries is stored globally in this module for each
+ * signal known to irssi-python. Each Script holds a list PY_SIGNAL_REC entries
+ * to remember signals and commands bound by the script. This allows for easy
+ * cleanup when the script is unloaded. The PY_SIGNAL_REC data includes
+ * references to a callable PyObject and a PY_SIGNAL_SPEC_REC entry for the
+ * signal.
+ *
+ * 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,
@@ -28,6 +52,26 @@ typedef struct _PY_SIGNAL_SPEC_REC
#include "pysigmap.h"
#define SIGNAME(sig) (sig->command? sig->command : sig->signal->name)
+/* This macro is useful for PY_SIGNAL_REC entries bound to "variable" signals,
+ * whose names extend the name prefix stored in the SPEC_REC entry.
+ *
+ * Example:
+ * sig->command == "command nick"
+ * sig->signal->name == "command "
+ *
+ * The exact signal text differs from the prefix, so the text must be stored
+ * separately in each PY_SIGNAL_REC entry.
+ *
+ * However, entries for "plain" signals do not require any extra data stored,
+ * so sig->command is NULL.
+ *
+ * Example:
+ * sig->command == NULL;
+ * sig->signal->name == "massjoin";
+ *
+ * "massjoin" is not a prefix for any signal, so any PY_SIGNAL_REC
+ * referencing the "massjoin" SPEC_REC entry will have a NULL command.
+ */
/* hashtable for normal signals, tree for variable signal prefixes. */
static GHashTable *py_sighash = NULL;
@@ -42,11 +86,12 @@ static PY_SIGNAL_REC *py_signal_rec_new(const char *signal, PyObject *func, cons
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_py2i(char code, PyObject *pobj, int arg);
+static void *py_py2i(char code, PyObject *pobj, int arg, const char *signal);
static void py_getstrlist(GList **list, PyObject *pylist);
static int precmp(const char *spec, const char *test);
static PY_SIGNAL_SPEC_REC *py_signal_lookup(const char *name);
static void py_signal_remove(PY_SIGNAL_SPEC_REC *sig);
+static int py_convert_args(void **args, PyObject *argtup, const char *signal);
PY_SIGNAL_REC *pysignals_command_bind(const char *cmd, PyObject *func,
const char *category, int priority)
@@ -257,7 +302,7 @@ static PyObject *py_i2py(char code, void *iobj)
}
/* PyObject -> irssi obj*/
-static void *py_py2i(char code, PyObject *pobj, int arg)
+static void *py_py2i(char code, PyObject *pobj, int arg, const char *signal)
{
char *type;
@@ -266,7 +311,7 @@ static void *py_py2i(char code, PyObject *pobj, int arg)
switch (code)
{
- /* XXX: string doesn't (necssarily) persist */
+ /* XXX: string doesn't persist */
case 's':
type = "str";
if (PyString_Check(pobj)) return PyString_AsString(pobj);
@@ -363,8 +408,8 @@ static void *py_py2i(char code, PyObject *pobj, int arg)
return NULL;
}
- PyErr_Format(PyExc_TypeError, "expected type %s for arg %d, but got %s",
- type, arg, pobj->ob_type->tp_name);
+ PyErr_Format(PyExc_TypeError, "signal `%s': expected type %s for arg %d, but got %s",
+ signal, type, arg, pobj->ob_type->tp_name);
return NULL;
}
@@ -420,7 +465,7 @@ static void py_run_handler(PY_SIGNAL_REC *rec, void **args)
if (!ret)
goto error;
- /*XXX: reference arg handling not well tested */
+ /*XXX: IN/OUT arg handling not well tested */
for (i = 0, j = 0; i < arglen; i++)
{
GList **list;
@@ -474,16 +519,13 @@ static void py_sig_proxy(void *p1, void *p2, void *p3, void *p4, void *p5, void
py_run_handler(rec, args);
}
-int pysignals_emit(const char *signal, PyObject *argtup)
+static int py_convert_args(void **args, PyObject *argtup, const char *signal)
{
- PY_SIGNAL_SPEC_REC *spec;
- void *args[6];
char *arglist;
+ PY_SIGNAL_SPEC_REC *spec;
int i;
int maxargs;
- memset(args, 0, sizeof args);
-
spec = py_signal_lookup(signal);
if (!spec)
{
@@ -491,17 +533,62 @@ int pysignals_emit(const char *signal, PyObject *argtup)
return 0;
}
+ /*XXX: specifying fewer signal args than in the format implicitly
+ sets overlooked args to NULL or 0 */
+
arglist = spec->arglist;
maxargs = strlen(arglist);
-
- for (i = 0; i < maxargs && i+1 < PyTuple_Size(argtup); i++)
+ for (i = 0; i < maxargs && i < PyTuple_Size(argtup); i++)
{
- args[i] = py_py2i(arglist[i], PyTuple_GET_ITEM(argtup, i+1), i+1);
+ args[i] = py_py2i(arglist[i],
+ PyTuple_GET_ITEM(argtup, i),
+ i+1, signal);
+
if (PyErr_Occurred()) /* XXX: any cleanup needed? */
- return 0;
+ return -1;
}
- signal_emit(signal, maxargs,
+ return maxargs;
+}
+
+int pysignals_emit(const char *signal, PyObject *argtup)
+{
+ int arglen;
+ void *args[6];
+
+ memset(args, 0, sizeof args);
+
+ arglen = py_convert_args(args, argtup, signal);
+ if (arglen < 0)
+ return 0;
+
+ signal_emit(signal, arglen,
+ args[0], args[1], args[2],
+ args[3], args[4], args[5]);
+
+ return 1;
+}
+
+int pysignals_continue(PyObject *argtup)
+{
+ const char *signal;
+ int arglen;
+ void *args[6];
+
+ memset(args, 0, sizeof args);
+
+ signal = signal_get_emitted();
+ if (!signal)
+ {
+ PyErr_Format(PyExc_LookupError, "cannot determine current signal");
+ return 0;
+ }
+
+ arglen = py_convert_args(args, argtup, signal);
+ if (arglen < 0)
+ return 0;
+
+ signal_continue(arglen,
args[0], args[1], args[2],
args[3], args[4], args[5]);