diff options
author | Christopher Davis <loafier@gmail.com> | 2006-07-03 06:00:18 +0000 |
---|---|---|
committer | Christopher Davis <loafier@gmail.com> | 2006-07-03 06:00:18 +0000 |
commit | 34cdeaae9369af5dce3ce3a4a7756c033bc44cc5 (patch) | |
tree | e6d1c6e41fa734e0ac40ef5f3c68aff689631017 | |
parent | a329fa4675852886ba2bbc9637cebcac882f1575 (diff) | |
download | irssi-python-34cdeaae9369af5dce3ce3a4a7756c033bc44cc5.tar.gz irssi-python-34cdeaae9369af5dce3ce3a4a7756c033bc44cc5.tar.xz irssi-python-34cdeaae9369af5dce3ce3a4a7756c033bc44cc5.zip |
worked on formats. working on themes now; testing adding functions and methods to objects
git-svn-id: http://svn.irssi.org/repos/irssi-python@4295 dbcabf3a-b0e7-0310-adc4-f8d773084564
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | constants.awk | 7 | ||||
-rw-r--r-- | constants.txt | 7 | ||||
-rw-r--r-- | irssi.py | 17 | ||||
-rw-r--r-- | objects/base-objects.h | 2 | ||||
-rw-r--r-- | objects/pyscript-object.c | 362 | ||||
-rw-r--r-- | objects/pyscript-object.h | 4 | ||||
-rw-r--r-- | objects/textdest-object.c | 129 | ||||
-rw-r--r-- | objects/textdest-object.h | 1 | ||||
-rw-r--r-- | objects/window-item-object.c | 3 | ||||
-rw-r--r-- | pyconstants.h | 7 | ||||
-rw-r--r-- | pycore.c | 3 | ||||
-rw-r--r-- | pyirssi.h | 3 | ||||
-rw-r--r-- | pyloader.c | 59 | ||||
-rw-r--r-- | pyloader.h | 2 | ||||
-rw-r--r-- | pymodule.c | 474 | ||||
-rw-r--r-- | pysignals.c | 37 | ||||
-rw-r--r-- | pysource.c | 166 | ||||
-rw-r--r-- | pysource.h | 7 | ||||
-rw-r--r-- | pythemes.c | 238 | ||||
-rw-r--r-- | pythemes.h | 13 |
21 files changed, 1310 insertions, 234 deletions
@@ -11,7 +11,8 @@ CFLAGS = -fpic -ggdb -Wall -I$(PYTHON) -I$(IRSSI) -I$(IRSSI)/src \ LDFLAGS = -fpic /usr/lib/libpython2.4.so -OBJ = pycore.o pyutils.o pymodule.o pyloader.o pysignals.o pysource.o +OBJ = pycore.o pyutils.o pymodule.o pyloader.o pysignals.o pysource.o \ +pythemes.o pyirssi: pyobjects.a $(OBJ) $(CC) -shared -o libirssi_python.so $(OBJ) objects/pyobjects.a $(LDFLAGS) diff --git a/constants.awk b/constants.awk index c9395c1..18f8002 100644 --- a/constants.awk +++ b/constants.awk @@ -3,7 +3,12 @@ BEGIN { } { - printf(" {\"%s\", %25s},\n", $1,$1); + if (NF >= 2) + constant = $2; + else + constant = $1; + + printf(" {\"%s\", %25s},\n", $1, constant); } END { diff --git a/constants.txt b/constants.txt index 3baff40..c65b779 100644 --- a/constants.txt +++ b/constants.txt @@ -1,5 +1,8 @@ -G_INPUT_READ -G_INPUT_WRITE +IO_IN G_IO_IN +IO_OUT G_IO_OUT +IO_PRI G_IO_PRI +IO_ERR G_IO_ERR +IO_HUP G_IO_HUP IRSSI_GUI_GNOME IRSSI_GUI_GTK IRSSI_GUI_KDE @@ -39,6 +39,23 @@ def command_bind(*args, **kwargs): """ see Script.command_bind """ get_script().command_bind(*args, **kwargs) +def command_unbind(*args, **kwargs): + """ see Script.command_unbind """ + get_script().command_unbind(*args, **kwargs) + def signal_add(*args, **kwargs): """ see Script.signal_add """ get_script().signal_add(*args, **kwargs) + +def signal_remove(*args, **kwargs): + """ see Script.signal_remove """ + get_script().signal_remove(*args, **kwargs) + +def timeout_add(*args, **kwargs): + """ see Script.timeout_add """ + get_script().timeout_add(*args, **kwargs) + +def io_add_watch(*args, **kwargs): + """ see Script.io_add_watch """ + get_script().io_add_watch(*args, **kwargs) + diff --git a/objects/base-objects.h b/objects/base-objects.h index ed2edfc..5351ead 100644 --- a/objects/base-objects.h +++ b/objects/base-objects.h @@ -27,7 +27,7 @@ typedef struct void *data; } PyIrssiObject; -#define DATA(obj) (((PyIrssiObject *)obj)->data) +#define DATA(obj) (obj? ((PyIrssiObject *)obj)->data : NULL) /* base for classes with a type */ typedef struct diff --git a/objects/pyscript-object.c b/objects/pyscript-object.c index 2ab7b88..d51761b 100644 --- a/objects/pyscript-object.c +++ b/objects/pyscript-object.c @@ -5,6 +5,7 @@ #include "pysignals.h" #include "pymodule.h" #include "pysource.h" +#include "pythemes.h" /* handle cycles... Can't think of any reason why the user would put script into one of the lists @@ -40,31 +41,29 @@ static void PyScript_dealloc(PyScript* self) static PyObject *PyScript_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyScript *self; - PyObject *argv, *modules; + PyObject *argv = NULL, *modules = NULL; argv = PyList_New(0); if (!argv) - return NULL; + goto error; modules = PyDict_New(); if (!modules) - { - Py_DECREF(argv); - return NULL; - } + goto error; self = (PyScript *)type->tp_alloc(type, 0); if (!self) - { - Py_DECREF(argv); - Py_DECREF(modules); - return NULL; - } - + goto error; + self->argv = argv; self->modules = modules; return (PyObject *)self; + +error: + Py_XDECREF(argv); + Py_XDECREF(modules); + return NULL; } PyDoc_STRVAR(PyScript_command_bind_doc, @@ -268,55 +267,70 @@ static PyObject *PyScript_signal_unregister(PyScript *self, PyObject *args, PyOb } PyDoc_STRVAR(PyScript_timeout_add_doc, - "timeout_add(msecs, func, data=None, once=False) -> int source handle\n" + "timeout_add(msecs, func, data=None) -> int source tag\n" + "\n" + "Add a timeout handler called every 'msecs' milliseconds until func\n" + "returns False or the source is removed with source_remove().\n" + "\n" + "func is called as func(data) or func(), depending on whether data\n" + "is specified or not.\n" ); static PyObject *PyScript_timeout_add(PyScript *self, PyObject *args, PyObject *kwds) { - static char *kwlist[] = {"msecs", "func", "data", "once", NULL}; + static char *kwlist[] = {"msecs", "func", "data", NULL}; int msecs = 0; PyObject *func = NULL; PyObject *data = NULL; - int once = 0; int ret; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "iO|Oi", kwlist, - &msecs, &func, &data, &once)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "iO|O", kwlist, + &msecs, &func, &data)) return NULL; if (msecs < 10) return PyErr_Format(PyExc_ValueError, "msecs must be at least 10"); - ret = pysource_timeout_add_list(&self->sources, msecs, func, data, once); + if (!PyCallable_Check(func)) + return PyErr_Format(PyExc_TypeError, "func not callable"); + + ret = pysource_timeout_add_list(&self->sources, msecs, func, data); return PyInt_FromLong(ret); } -PyDoc_STRVAR(PyScript_input_add_doc, - "input_add(fd, func, data=None, once=False, condition=G_INPUT_READ) -> int source tag\n" +PyDoc_STRVAR(PyScript_io_add_watch_doc, + "io_add_watch(fd, func, data=None, condition=IO_IN|IO_PRI) -> int source tag\n" ); -static PyObject *PyScript_input_add(PyScript *self, PyObject *args, PyObject *kwds) +static PyObject *PyScript_io_add_watch(PyScript *self, PyObject *args, PyObject *kwds) { - static char *kwlist[] = {"fd", "func", "data", "once", "condition", NULL}; + static char *kwlist[] = {"fd", "func", "data", "condition", NULL}; int fd = 0; + PyObject *pyfd = NULL; PyObject *func = NULL; PyObject *data = NULL; - int once = 0; - int condition = G_INPUT_READ; + int condition = G_IO_IN | G_IO_PRI; int ret; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "iO|Oii", kwlist, - &fd, &func, &data, &once, &condition)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|Oi", kwlist, + &pyfd, &func, &data, &condition)) return NULL; - ret = pysource_input_add_list(&self->sources, fd, condition, func, data, once); + fd = PyObject_AsFileDescriptor(pyfd); + if (fd < 0) + return NULL; + + if (!PyCallable_Check(func)) + return PyErr_Format(PyExc_TypeError, "func not callable"); + + ret = pysource_io_add_watch_list(&self->sources, fd, condition, func, data); return PyInt_FromLong(ret); } PyDoc_STRVAR(PyScript_source_remove_doc, - "source_remove(tag) -> None\n" + "source_remove(tag) -> bool\n" "\n" - "Remove IO or timeout source by tag\n" + "Remove IO or timeout source by tag. Return True if tag found and removed.\n" ); static PyObject *PyScript_source_remove(PyScript *self, PyObject *args, PyObject *kwds) { @@ -327,8 +341,204 @@ static PyObject *PyScript_source_remove(PyScript *self, PyObject *args, PyObject &tag)) return NULL; - if (!pysource_remove_tag(&self->sources, tag)) - return PyErr_Format(PyExc_KeyError, "source tag not found"); + /* the destroy notify func will remove the list link, but first + check that the tag exists in this Script object */ + if (g_slist_find(self->sources, GINT_TO_POINTER(tag))) + return PyBool_FromLong(g_source_remove(tag)); + + Py_RETURN_FALSE; +} + +static int py_settings_add(PyScript *self, const char *name) +{ + GSList *node; + + node = gslist_find_icase_string(self->settings, name); + if (node) + return 0; + + self->settings = g_slist_append(self->settings, g_strdup(name)); + + return 1; +} + +static int py_settings_remove(PyScript *self, const char *name) +{ + GSList *node; + + node = gslist_find_icase_string(self->settings, name); + if (!node) + return 0; + + settings_remove(node->data); + g_free(node->data); + + self->settings = g_slist_delete_link(self->settings, node); + + return 1; +} + +PyDoc_STRVAR(PyScript_settings_add_str_doc, + "" +); +static PyObject *PyScript_settings_add_str(PyScript *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"section", "key", "def", NULL}; + char *section = ""; + char *key = ""; + char *def = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "sss", kwlist, + §ion, &key, &def)) + return NULL; + + if (!py_settings_add(self, key)) + return PyErr_Format(PyExc_ValueError, "key, %s, already added by script", key); + + settings_add_str_module(MODULE_NAME"/scripts", section, key, def); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyScript_settings_add_int_doc, + "" +); +static PyObject *PyScript_settings_add_int(PyScript *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"section", "key", "def", NULL}; + char *section = ""; + char *key = ""; + int def = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ssi", kwlist, + §ion, &key, &def)) + return NULL; + + if (!py_settings_add(self, key)) + return PyErr_Format(PyExc_ValueError, "key, %s, already added by script", key); + + settings_add_int_module(MODULE_NAME"/scripts", section, key, def); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyScript_settings_add_bool_doc, + "" +); +static PyObject *PyScript_settings_add_bool(PyScript *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"section", "key", "def", NULL}; + char *section = ""; + char *key = ""; + int def = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ssi", kwlist, + §ion, &key, &def)) + return NULL; + + if (!py_settings_add(self, key)) + return PyErr_Format(PyExc_ValueError, "key, %s, already added by script", key); + + settings_add_bool_module(MODULE_NAME"/scripts", section, key, def); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyScript_settings_add_time_doc, + "" +); +static PyObject *PyScript_settings_add_time(PyScript *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"section", "key", "def", NULL}; + char *section = ""; + char *key = ""; + char *def = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "sss", kwlist, + §ion, &key, &def)) + return NULL; + + if (!py_settings_add(self, key)) + return PyErr_Format(PyExc_ValueError, "key, %s, already added by script", key); + + settings_add_time_module(MODULE_NAME"/scripts", section, key, def); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyScript_settings_add_level_doc, + "" +); +static PyObject *PyScript_settings_add_level(PyScript *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"section", "key", "def", NULL}; + char *section = ""; + char *key = ""; + char *def = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "sss", kwlist, + §ion, &key, &def)) + return NULL; + + if (!py_settings_add(self, key)) + return PyErr_Format(PyExc_ValueError, "key, %s, already added by script", key); + + settings_add_level_module(MODULE_NAME"/scripts", section, key, def); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyScript_settings_add_size_doc, + "" +); +static PyObject *PyScript_settings_add_size(PyScript *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"section", "key", "def", NULL}; + char *section = ""; + char *key = ""; + char *def = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "sss", kwlist, + §ion, &key, &def)) + return NULL; + + if (!py_settings_add(self, key)) + return PyErr_Format(PyExc_ValueError, "key, %s, already added by script", key); + + settings_add_size_module(MODULE_NAME"/scripts", section, key, def); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyScript_settings_remove_doc, + "settings_remove(key) -> bool\n" +); +static PyObject *PyScript_settings_remove(PyScript *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"key", NULL}; + char *key = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &key)) + return NULL; + + return PyBool_FromLong(py_settings_remove(self, key)); +} + +PyDoc_STRVAR(PyScript_theme_register_doc, + "theme_register(list) -> None\n" +); +static PyObject *PyScript_theme_register(PyScript *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"list", NULL}; + PyObject *list = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, + &list)) + return NULL; + + if (!pythemes_register(pyscript_get_name(self), list)) + return NULL; Py_RETURN_NONE; } @@ -349,10 +559,26 @@ static PyMethodDef PyScript_methods[] = { PyScript_signal_unregister_doc}, {"timeout_add", (PyCFunction)PyScript_timeout_add, METH_VARARGS | METH_KEYWORDS, PyScript_timeout_add_doc}, - {"input_add", (PyCFunction)PyScript_input_add, METH_VARARGS | METH_KEYWORDS, - PyScript_input_add_doc}, + {"io_add_watch", (PyCFunction)PyScript_io_add_watch, METH_VARARGS | METH_KEYWORDS, + PyScript_io_add_watch_doc}, {"source_remove", (PyCFunction)PyScript_source_remove, METH_VARARGS | METH_KEYWORDS, PyScript_source_remove_doc}, + {"settings_add_str", (PyCFunction)PyScript_settings_add_str, METH_VARARGS | METH_KEYWORDS, + PyScript_settings_add_str_doc}, + {"settings_add_int", (PyCFunction)PyScript_settings_add_int, METH_VARARGS | METH_KEYWORDS, + PyScript_settings_add_int_doc}, + {"settings_add_bool", (PyCFunction)PyScript_settings_add_bool, METH_VARARGS | METH_KEYWORDS, + PyScript_settings_add_bool_doc}, + {"settings_add_time", (PyCFunction)PyScript_settings_add_time, METH_VARARGS | METH_KEYWORDS, + PyScript_settings_add_time_doc}, + {"settings_add_level", (PyCFunction)PyScript_settings_add_level, METH_VARARGS | METH_KEYWORDS, + PyScript_settings_add_level_doc}, + {"settings_add_size", (PyCFunction)PyScript_settings_add_size, METH_VARARGS | METH_KEYWORDS, + PyScript_settings_add_size_doc}, + {"settings_remove", (PyCFunction)PyScript_settings_remove, METH_VARARGS | METH_KEYWORDS, + PyScript_settings_remove_doc}, + {"theme_register", (PyCFunction)PyScript_theme_register, METH_VARARGS | METH_KEYWORDS, + PyScript_theme_register_doc}, {NULL} /* Sentinel */ }; @@ -363,7 +589,7 @@ static PyMemberDef PyScript_members[] = { {NULL} /* Sentinel */ }; -static PyTypeObject PyScriptType = { +PyTypeObject PyScriptType = { PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ "Script", /*tp_name*/ @@ -408,7 +634,7 @@ static PyTypeObject PyScriptType = { /* PyScript factory function */ PyObject *pyscript_new(PyObject *module, char **argv) { - PyObject *script = NULL; + PyObject *script; script = PyObject_CallFunction((PyObject*)&PyScriptType, "()"); @@ -418,17 +644,20 @@ PyObject *pyscript_new(PyObject *module, char **argv) while (*argv) { - PyObject *str = PyString_FromString(*argv); - if (!str) + if (**argv != '\0') { - /* The destructor should DECREF argv */ - Py_DECREF(script); - return NULL; + PyObject *str = PyString_FromString(*argv); + if (!str) + { + /* The destructor should DECREF argv */ + Py_DECREF(script); + return NULL; + } + + PyList_Append(scr->argv, str); + Py_DECREF(str); } - PyList_Append(scr->argv, str); - Py_DECREF(str); - *argv++; } @@ -466,15 +695,47 @@ void pyscript_remove_signals(PyObject *script) void pyscript_remove_sources(PyObject *script) { + GSList *node; + PyScript *self; + + g_return_if_fail(pyscript_check(script)); + + self = (PyScript *) script; + + node = self->sources; + while (node) + { + /* the notify func will destroy the link so save next */ + GSList *next = node->next; + g_source_remove(GPOINTER_TO_INT(node->data)); + node = next; + } + + g_return_if_fail(self->sources == NULL); +} + +void pyscript_remove_settings(PyObject *script) +{ PyScript *self; g_return_if_fail(pyscript_check(script)); self = (PyScript *) script; - pysource_remove_list(self->sources); - g_slist_free(self->sources); - self->sources = NULL; + g_slist_foreach(self->settings, (GFunc)settings_remove, NULL); + g_slist_foreach(self->settings, (GFunc)g_free, NULL); + g_slist_free(self->settings); +} + +void pyscript_remove_themes(PyObject *script) +{ + PyScript *self; + + g_return_if_fail(pyscript_check(script)); + + self = (PyScript *) script; + + pythemes_unregister(pyscript_get_name(script)); } void pyscript_clear_modules(PyObject *script) @@ -488,6 +749,15 @@ void pyscript_clear_modules(PyObject *script) PyDict_Clear(self->modules); } +void pyscript_cleanup(PyObject *script) +{ + pyscript_remove_signals(script); + pyscript_remove_sources(script); + pyscript_remove_settings(script); + pyscript_remove_themes(script); + pyscript_clear_modules(script); +} + int pyscript_init(void) { if (PyType_Ready(&PyScriptType) < 0) diff --git a/objects/pyscript-object.h b/objects/pyscript-object.h index fafa94b..644571d 100644 --- a/objects/pyscript-object.h +++ b/objects/pyscript-object.h @@ -11,6 +11,7 @@ typedef struct { GSList *signals; /* list of bound signals and commands */ GSList *registered_signals; /* list of signal names registered */ GSList *sources; /* list of io and timeout sources */ + GSList *settings; /* list of settings from settings_add_*() */ } PyScript; extern PyTypeObject PyScriptType; @@ -19,7 +20,10 @@ int pyscript_init(void); PyObject *pyscript_new(PyObject *module, char **argv); void pyscript_remove_signals(PyObject *script); void pyscript_remove_sources(PyObject *script); +void pyscript_remove_settings(PyObject *script); +void pyscript_remove_themes(PyObject *script); void pyscript_clear_modules(PyObject *script); +void pyscript_cleanup(PyObject *script); #define pyscript_check(op) PyObject_TypeCheck(op, &PyScriptType) #define pyscript_get_name(scr) PyModule_GetName(((PyScript*)scr)->module) #define pyscript_get_filename(scr) PyModule_GetFilename(((PyScript*)scr)->module) diff --git a/objects/textdest-object.c b/objects/textdest-object.c index 9dd94d6..2ad6523 100644 --- a/objects/textdest-object.c +++ b/objects/textdest-object.c @@ -5,13 +5,20 @@ #include "factory.h" #include "pycore.h" -/* XXX: no cleanup signal for textdest */ +static int pytextdest_setup(PyTextDest *pytdest, void *td, int owned); +/* XXX: no cleanup signal for textdest */ static void PyTextDest_dealloc(PyTextDest *self) { Py_XDECREF(self->window); Py_XDECREF(self->server); - + + if (self->owned) + { + g_free((char*)self->data->target); + g_free(self->data); + } + self->ob_type->tp_free((PyObject*)self); } @@ -26,6 +33,57 @@ static PyObject *PyTextDest_new(PyTypeObject *type, PyObject *args, PyObject *kw return (PyObject *)self; } +/* init function to create the textdest */ +PyDoc_STRVAR(PyTextDest_doc, + "__init__(target, level=MSGLEVEL_CLIENTNOTICE, server=None, window=None)\n" + "\n" + "Create a TextDest\n" +); +static int PyTextDest_init(PyTextDest *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"target", "level", "server", "window", NULL}; + char *target; + int level = MSGLEVEL_CLIENTNOTICE; + PyObject *server = NULL, *window = NULL; + TEXT_DEST_REC *dest; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|ioo", kwlist, + &target, &level, &server, &window)) + return -1; + + if (server == Py_None) + server = NULL; + if (window == Py_None) + window = NULL; + + if (server && !pyserver_check(server)) + { + PyErr_Format(PyExc_TypeError, "arg 3 isnt server"); + return -1; + } + + if (window && !pywindow_check(window)) + { + PyErr_Format(PyExc_TypeError, "arg 4 isnt window"); + return -1; + } + + if (self->data) + { + PyErr_Format(PyExc_RuntimeError, "TextDest already wrapped"); + return -1; + } + + dest = g_new0(TEXT_DEST_REC, 1); + format_create_dest(dest, DATA(server), g_strdup(target), level, DATA(window)); + + if (!pytextdest_setup(self, dest, 1)) + return -1; + + return 0; +} + +/* Getters */ PyDoc_STRVAR(PyTextDest_window_doc, "Window where the text will be written" ); @@ -97,8 +155,32 @@ static PyGetSetDef PyTextDest_getseters[] = { {NULL} }; +/* Methods */ +PyDoc_STRVAR(PyTextDest_prnt_doc, + "prnt(str) -> None\n" + "\n" + "Print str to TextDest\n" +); +static PyObject *PyTextDest_prnt(PyTextDest *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"str", NULL}; + char *str = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &str)) + return NULL; + + printtext_dest(self->data, "%s", str); + + Py_RETURN_NONE; +} + /* Methods for object */ static PyMethodDef PyTextDest_methods[] = { + {"prnt", (PyCFunction)PyTextDest_prnt, METH_VARARGS | METH_KEYWORDS, + PyTextDest_prnt_doc}, {NULL} /* Sentinel */ }; @@ -124,7 +206,7 @@ PyTypeObject PyTextDestType = { 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - "PyTextDest objects", /* tp_doc */ + PyTextDest_doc, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ @@ -139,38 +221,53 @@ PyTypeObject PyTextDestType = { 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ - 0, /* tp_init */ + (initproc)PyTextDest_init, /* tp_init */ 0, /* tp_alloc */ PyTextDest_new, /* tp_new */ }; - -/* TextDest factory function */ -PyObject *pytextdest_new(void *td) +static int pytextdest_setup(PyTextDest *pytdest, void *td, int owned) { PyObject *window, *server; - PyTextDest *pytdest; TEXT_DEST_REC *tdest = td; - window = py_irssi_chat_new(tdest->window, 1); - if (!window) - return NULL; + if (tdest->window) + { + window = pywindow_new(tdest->window); + if (!window) + return 0; + } server = py_irssi_chat_new(tdest->server, 1); if (!server) { Py_DECREF(window); - return NULL; + return 0; } - pytdest = py_inst(PyTextDest, PyTextDestType); - if (!pytdest) - return NULL; - pytdest->data = td; pytdest->window = window; pytdest->server = server; + pytdest->owned = owned; + return 1; +} + +/* TextDest factory function */ +PyObject *pytextdest_new(void *td) +{ + PyTextDest *pytdest; + + pytdest = py_inst(PyTextDest, PyTextDestType); + if (!pytdest) + return NULL; + + if (!pytextdest_setup(pytdest, td, 0)) + { + Py_DECREF(pytdest); + return NULL; + } + return (PyObject *)pytdest; } diff --git a/objects/textdest-object.h b/objects/textdest-object.h index 366914a..3e273e6 100644 --- a/objects/textdest-object.h +++ b/objects/textdest-object.h @@ -12,6 +12,7 @@ typedef struct PyIrssiFinal_HEAD(struct _TEXT_DEST_REC) PyObject *window; PyObject *server; + int owned; } PyTextDest; extern PyTypeObject PyTextDestType; diff --git a/objects/window-item-object.c b/objects/window-item-object.c index 6494d48..7acb075 100644 --- a/objects/window-item-object.c +++ b/objects/window-item-object.c @@ -69,6 +69,7 @@ static PyGetSetDef PyWindowItem_getseters[] = { {NULL} }; +/* Methods */ PyDoc_STRVAR(PyWindowItem_prnt_doc, "Print to window item" ); @@ -106,8 +107,6 @@ static PyObject *PyWindowItem_command(PyWindowItem *self, PyObject *args, PyObje Py_RETURN_NONE; } -/* Methods */ - PyDoc_STRVAR(PyWindowItem_window_doc, "Return parent window for window item" ); diff --git a/pyconstants.h b/pyconstants.h index 4cf6407..e196236 100644 --- a/pyconstants.h +++ b/pyconstants.h @@ -1,6 +1,9 @@ static PY_CONSTANT_REC py_constants[] = { - {"INPUT_READ", INPUT_READ}, - {"INPUT_WRITE", INPUT_WRITE}, + {"IO_IN", G_IO_IN}, + {"IO_OUT", G_IO_OUT}, + {"IO_PRI", G_IO_PRI}, + {"IO_ERR", G_IO_ERR}, + {"IO_HUP", G_IO_HUP}, {"IRSSI_GUI_GNOME", IRSSI_GUI_GNOME}, {"IRSSI_GUI_GTK", IRSSI_GUI_GTK}, {"IRSSI_GUI_KDE", IRSSI_GUI_KDE}, @@ -7,6 +7,7 @@ #include "pyloader.h" #include "pymodule.h" #include "pysignals.h" +#include "pythemes.h" #include "factory.h" /*XXX: copy parse into utils */ @@ -118,7 +119,7 @@ void irssi_python_init(void) Py_InitializeEx(0); pysignals_init(); - if (!pyloader_init() || !pymodule_init() || !factory_init()) + if (!pyloader_init() || !pymodule_init() || !factory_init() || !pythemes_init()) { printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Failed to load Python"); return; @@ -6,6 +6,7 @@ #include "common.h" #include "modules.h" #include "commands.h" +#include "settings.h" #include "printtext.h" #include "window-items.h" #include "window-activity.h" @@ -18,9 +19,11 @@ #include "chatnets.h" #include "servers-reconnect.h" #include "masks.h" +#include "misc.h" #include "rawlog.h" #include "log.h" #include "ignore.h" #include "fe-exec.h" +#include "pidwait.h" #endif @@ -1,4 +1,5 @@ #include <Python.h> +#include <frameobject.h> #include <string.h> #include "pyirssi.h" #include "pyloader.h" @@ -70,7 +71,7 @@ static char *py_find_script(const char *name) fname = (char *)name; /*XXX: use case insensitive path search? */ - for (node = script_paths; node != NULL && !path; node = node->next) + for (node = script_paths; node && !path; node = node->next) { path = g_strdup_printf("%s/%s", (char *)node->data, fname); @@ -113,31 +114,32 @@ int pyloader_load_script_argv(char **argv) name = file_get_filename(path); module = PyModule_New(name); g_free(name); - if (!module) goto error; script = pyscript_new(module, argv); Py_DECREF(module); - if (!script) goto error; /* insert script obj into module dict, load file */ - if (PyModule_AddObject(module, "_script", script) < 0) + if (PyModule_AddObject(module, "_script", script) != 0) goto error; + Py_INCREF(script); if (!py_load_module(module, path)) goto error; - PyList_Append(script_modules, script); - Py_DECREF(script); - g_free(path); + if (PyList_Append(script_modules, script) != 0) + goto error; + printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "loaded script %s", argv[0]); /* PySys_WriteStdout("load %s, script -> 0x%x\n", argv[0], script); */ - printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "loaded script %s", argv[0]); + Py_DECREF(script); + g_free(path); + return 1; error: @@ -200,9 +202,7 @@ int pyloader_unload_script(const char *name) PySys_WriteStdout("unload %s, script -> 0x%x\n", name, script); - pyscript_remove_signals(script); - pyscript_remove_sources(script); - pyscript_clear_modules(script); + pyscript_cleanup(script); if (PySequence_DelItem(script_modules, id) < 0) { @@ -218,6 +218,39 @@ int pyloader_unload_script(const char *name) return 1; } +/* Traverse stack backwards to find the nearest valid _script object in globals */ +PyObject *pyloader_find_script_obj(void) +{ + PyFrameObject *frame; + + for (frame = PyEval_GetFrame(); frame != NULL; frame = frame->f_back) + { + g_return_val_if_fail(frame->f_globals != NULL, NULL); + PyObject *script = PyDict_GetItemString(frame->f_globals, "_script"); + if (script && pyscript_check(script)) + { + /* + PySys_WriteStdout("Found script at %s in %s, script -> 0x%x\n", + PyString_AS_STRING(frame->f_code->co_name), + PyString_AS_STRING(frame->f_code->co_filename), script); + */ + return script; + } + } + + return NULL; +} + +char *pyloader_find_script_name(void) +{ + PyObject *script = pyloader_find_script_obj(); + + if (!script) + return NULL; + + return pyscript_get_name(script); +} + GSList *pyloader_list(void) { int i; @@ -296,9 +329,7 @@ static void py_clear_scripts() for (i = 0; i < PyList_Size(script_modules); i++) { PyObject *scr = PyList_GET_ITEM(script_modules, i); - pyscript_remove_signals(scr); - pyscript_remove_sources(scr); - pyscript_clear_modules(scr); + pyscript_cleanup(scr); } Py_DECREF(script_modules); @@ -11,6 +11,8 @@ void pyloader_add_script_path(const char *path); int pyloader_load_script_argv(char **argv); int pyloader_load_script(char *name); int pyloader_unload_script(const char *name); +PyObject *pyloader_find_script_obj(void); +char *pyloader_find_script_name(void); GSList *pyloader_list(void); void pyloader_list_destroy(GSList **list); @@ -1,17 +1,18 @@ #include <Python.h> -#include <frameobject.h> #include "pymodule.h" #include "pyirssi_irc.h" #include "pyscript-object.h" #include "factory.h" #include "pyutils.h" #include "pysignals.h" +#include "pyloader.h" +#include "pythemes.h" /* * This module is some what different than the Perl's. * Script specific operations are handled by the Script object * instead of by a function in this module. command_bind, - * signal_bind, etc require data to be saved about the script + * signal_add, etc require data to be saved about the script * for cleanup purposes, so I moved those functions to the script * object. */ @@ -19,8 +20,6 @@ /* Main embedded module */ PyObject *py_module = NULL; -static PyObject *find_script(void); - /* Module functions */ /*XXX: prefix PY to avoid ambiguity with py_command function */ PyDoc_STRVAR(PY_command_doc, @@ -64,7 +63,7 @@ PyDoc_STRVAR(py_get_script_doc, ); static PyObject *py_get_script(PyObject *self, PyObject *args) { - PyObject *ret = find_script(); + PyObject *ret = pyloader_find_script_obj(); /* XXX: type check */ @@ -949,6 +948,411 @@ static PyObject *py_signal_get_emitted_id(PyObject *self, PyObject *args) return PyInt_FromLong(signal_get_emitted_id()); } +PyDoc_STRVAR(py_settings_get_str_doc, + "settings_get_str(key) -> str\n" + "\n" + "Get value for setting.\n" +); +static PyObject *py_settings_get_str(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"key", NULL}; + char *key = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &key)) + return NULL; + + RET_AS_STRING_OR_NONE(settings_get_str(key)); +} + +PyDoc_STRVAR(py_settings_get_int_doc, + "settings_get_int(key) -> int\n" + "\n" + "Get value for setting." +); +static PyObject *py_settings_get_int(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"key", NULL}; + char *key = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &key)) + return NULL; + + return PyInt_FromLong(settings_get_int(key)); +} + +PyDoc_STRVAR(py_settings_get_bool_doc, + "settings_get_bool(key) -> bool\n" + "\n" + "Get value for setting.\n" +); +static PyObject *py_settings_get_bool(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"key", NULL}; + char *key = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &key)) + return NULL; + + return PyBool_FromLong(settings_get_bool(key)); +} + +PyDoc_STRVAR(py_settings_get_time_doc, + "settings_get_time(key) -> long\n" + "\n" + "Get value for setting.\n" +); +static PyObject *py_settings_get_time(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"key", NULL}; + char *key = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &key)) + return NULL; + + return PyLong_FromLong(settings_get_time(key)); +} + +PyDoc_STRVAR(py_settings_get_level_doc, + "settings_get_level(key) -> int\n" + "\n" + "Get value for setting.\n" +); +static PyObject *py_settings_get_level(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"key", NULL}; + char *key = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &key)) + return NULL; + + return PyInt_FromLong(settings_get_level(key)); +} + +PyDoc_STRVAR(py_settings_get_size_doc, + "settings_get_size(key) -> long\n" + "\n" + "Get value for setting.\n" +); +static PyObject *py_settings_get_size(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"key", NULL}; + char *key = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &key)) + return NULL; + + return PyLong_FromLong(settings_get_size(key)); +} + +PyDoc_STRVAR(py_settings_set_str_doc, + "settings_set_str(key, value) -> None\n" + "\n" + "Set string value for setting\n" +); +static PyObject *py_settings_set_str(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"key", "value", NULL}; + char *key = ""; + char *value = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist, + &key, &value)) + return NULL; + + settings_set_str(key, value); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(py_settings_set_int_doc, + "settings_set_int(key, value) -> None\n" + "\n" + "Set int value for setting" +); +static PyObject *py_settings_set_int(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"key", "value", NULL}; + char *key = ""; + int value = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "si", kwlist, + &key, &value)) + return NULL; + + settings_set_int(key, value); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(py_settings_set_bool_doc, + "settings_set_bool(key, value) -> None\n" + "\n" + "Set bool value for setting\n" +); +static PyObject *py_settings_set_bool(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"key", "value", NULL}; + char *key = ""; + int value = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "si", kwlist, + &key, &value)) + return NULL; + + settings_set_bool(key, value); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(py_settings_set_time_doc, + "settings_set_time(key, value) -> bool\n" + "\n" + "Set string value for setting\n" +); +static PyObject *py_settings_set_time(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"key", "value", NULL}; + char *key = ""; + char *value = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist, + &key, &value)) + return NULL; + + return PyBool_FromLong(settings_set_time(key, value)); +} + +PyDoc_STRVAR(py_settings_set_level_doc, + "settings_set_level(key, value) -> bool\n" + "\n" + "Set string value for setting\n" +); +static PyObject *py_settings_set_level(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"key", "value", NULL}; + char *key = ""; + char *value = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist, + &key, &value)) + return NULL; + + return PyBool_FromLong(settings_set_level(key, value)); +} + +PyDoc_STRVAR(py_settings_set_size_doc, + "settings_set_size(key, value) -> bool\n" + "\n" + "Set string value for setting\n" +); +static PyObject *py_settings_set_size(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"key", "value", NULL}; + char *key = ""; + char *value = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist, + &key, &value)) + return NULL; + + return PyBool_FromLong(settings_set_size(key, value)); +} + +PyDoc_STRVAR(py_pidwait_add_doc, + "pidwait_add(pid) -> None\n" + "\n" + "Add pid to wait list\n" +); +static PyObject *py_pidwait_add(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"pid", NULL}; + int pid = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, + &pid)) + return NULL; + + pidwait_add(pid); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(py_pidwait_remove_doc, + "pidwait_remove(pid) -> None\n" + "\n" + "Remove pid from wait list\n" +); +static PyObject *py_pidwait_remove(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"pid", NULL}; + int pid = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, + &pid)) + return NULL; + + pidwait_remove(pid); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(py_format_get_length_doc, + "format_get_length(str) -> int length\n" +); +static PyObject *py_format_get_length(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"str", NULL}; + char *str = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &str)) + return NULL; + + return PyInt_FromLong(format_get_length(str)); +} + +PyDoc_STRVAR(py_format_real_length_doc, + "format_real_length(str, len) -> int length\n" +); +static PyObject *py_format_real_length(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"str", "len", NULL}; + char *str = ""; + int len; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "sl", kwlist, + &str, &len)) + return NULL; + + return PyInt_FromLong(format_real_length(str, len)); +} + +PyDoc_STRVAR(py_strip_codes_doc, + "strip_codes(input) -> str\n" +); +static PyObject *py_strip_codes(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"input", NULL}; + char *input = ""; + char *ret; + PyObject *pyret; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &input)) + return NULL; + + ret = strip_codes(input); + if (ret) + { + pyret = PyString_FromString(ret); + g_free(ret); + return pyret; + } + + Py_RETURN_NONE; +} + +#if 0 +PyDoc_STRVAR(py_format_get_text_doc, + "format_get_text(textdest, module, formatnum, ...) -> str\n" + "\n" + "Return a substituted format string from module and formatnum.\n" + "module is a string.\n" + "\n" + "Example:\n" + "TODO\n" +); +static PyObject *py_format_get_text(PyObject *self, PyObject *varargs) +{ + PyTextDest *textdest = NULL; + char *module = ""; + unsigned int formatnum = 0; + PyObject *args = NULL, *pycharargs = NULL; + THEME_REC *theme; + MODULE_THEME_REC *modtheme; + char **charargs = NULL; + char *ret; + int i; + + args = PySequence_GetSlice(varargs, 0, 3); + if (!args) + goto error; + + pycharargs = PySequence_GetSlice(varargs, 3, PyTuple_Size(varargs)); + if (!pycharargs) + goto error; + + if (!PyArg_ParseTuple(args, "OsI", + &textdest, &module, &formatnum)) + goto error; + + if (!pytextdest_check((PyObject *)textdest)) + { + PyErr_Format(PyExc_TypeError, "arg 1 must be TextDest"); + goto error; + } + + /* Bleh, check that formatnum is within range */ + theme = window_get_theme(textdest->data->window); + + /* FIXME: how to boundscheck formatnum ?? */ + + /* size + 1 for terminating NULL ptr */ + charargs = g_new0(char *, PyTuple_Size(pycharargs) + 1); + + for (i = 0; i < PyTuple_Size(pycharargs); i++) + { + PyObject *obj = PyTuple_GET_ITEM(pycharargs, i); + char *str; + + if (!PyString_Check(obj)) + { + PyErr_Format(PyExc_TypeError, + "non string in string argument list (arg %d)", + i + 4); + goto error; + } + + str = PyString_AsString(obj); + if (!str) + goto error; + + charargs[i] = str; + } + + /* return string, or if string is NULL, return None */ + ret = format_get_text_theme_charargs(theme, module, DATA(textdest), formatnum, charargs); + Py_DECREF(args); + Py_DECREF(pycharargs); + g_free(charargs); + + if (ret) + { + PyObject *pyret; + + pyret = PyString_FromString(ret); + g_free(ret); + + return pyret; + } + + Py_RETURN_NONE; + +error: + Py_XDECREF(args); + Py_XDECREF(pycharargs); + g_free(charargs); + + return NULL; +} +#endif + static PyMethodDef ModuleMethods[] = { {"prnt", (PyCFunction)py_prnt, METH_VARARGS | METH_KEYWORDS, py_prnt_doc}, @@ -1066,33 +1470,45 @@ static PyMethodDef ModuleMethods[] = { py_signal_get_emitted_id_doc}, {"signal_continue", (PyCFunction)py_signal_continue, METH_VARARGS, py_signal_continue_doc}, + {"settings_get_str", (PyCFunction)py_settings_get_str, METH_VARARGS | METH_KEYWORDS, + py_settings_get_str_doc}, + {"settings_get_int", (PyCFunction)py_settings_get_int, METH_VARARGS | METH_KEYWORDS, + py_settings_get_int_doc}, + {"settings_get_bool", (PyCFunction)py_settings_get_bool, METH_VARARGS | METH_KEYWORDS, + py_settings_get_bool_doc}, + {"settings_get_time", (PyCFunction)py_settings_get_time, METH_VARARGS | METH_KEYWORDS, + py_settings_get_time_doc}, + {"settings_get_level", (PyCFunction)py_settings_get_level, METH_VARARGS | METH_KEYWORDS, + py_settings_get_level_doc}, + {"settings_get_size", (PyCFunction)py_settings_get_size, METH_VARARGS | METH_KEYWORDS, + py_settings_get_size_doc}, + {"settings_set_str", (PyCFunction)py_settings_set_str, METH_VARARGS | METH_KEYWORDS, + py_settings_set_str_doc}, + {"settings_set_int", (PyCFunction)py_settings_set_int, METH_VARARGS | METH_KEYWORDS, + py_settings_set_int_doc}, + {"settings_set_bool", (PyCFunction)py_settings_set_bool, METH_VARARGS | METH_KEYWORDS, + py_settings_set_bool_doc}, + {"settings_set_time", (PyCFunction)py_settings_set_time, METH_VARARGS | METH_KEYWORDS, + py_settings_set_time_doc}, + {"settings_set_level", (PyCFunction)py_settings_set_level, METH_VARARGS | METH_KEYWORDS, + py_settings_set_level_doc}, + {"settings_set_size", (PyCFunction)py_settings_set_size, METH_VARARGS | METH_KEYWORDS, + py_settings_set_size_doc}, + {"pidwait_add", (PyCFunction)py_pidwait_add, METH_VARARGS | METH_KEYWORDS, + py_pidwait_add_doc}, + {"pidwait_remove", (PyCFunction)py_pidwait_remove, METH_VARARGS | METH_KEYWORDS, + py_pidwait_remove_doc}, + {"format_get_length", (PyCFunction)py_format_get_length, METH_VARARGS | METH_KEYWORDS, + py_format_get_length_doc}, + {"format_real_length", (PyCFunction)py_format_real_length, METH_VARARGS | METH_KEYWORDS, + py_format_real_length_doc}, + {"strip_codes", (PyCFunction)py_strip_codes, METH_VARARGS | METH_KEYWORDS, + py_strip_codes_doc}, + /*{"format_get_text", (PyCFunction)py_format_get_text, METH_VARARGS, + py_format_get_text_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) -{ - PyFrameObject *frame; - - for (frame = PyEval_GetFrame(); frame != NULL; frame = frame->f_back) - { - g_return_val_if_fail(frame->f_globals != NULL, NULL); - PyObject *script = PyDict_GetItemString(frame->f_globals, "_script"); - if (script) - { - /* - PySys_WriteStdout("Found script at %s in %s, script -> 0x%x\n", - PyString_AS_STRING(frame->f_code->co_name), - PyString_AS_STRING(frame->f_code->co_filename), script); - */ - return script; - } - } - - return NULL; -} - int pymodule_init(void) { g_return_val_if_fail(py_module == NULL, 0); diff --git a/pysignals.c b/pysignals.c index 4e6d4ee..95718aa 100644 --- a/pysignals.c +++ b/pysignals.c @@ -6,12 +6,13 @@ /* NOTE: * 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. + * signal, or a type of "variable" signal. Each PY_SIGNAL_REC stores data + * about a handler for a 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. + * Plain signals are emitted using the same text every time. 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 @@ -21,23 +22,23 @@ * 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). + * PY_SIGNAL_REC stores data about each handler 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. + * signal known to irssi-python. Each Script holds a list of PY_SIGNAL_REC + * entries to remember signals and commands bound by the script. The + * PY_SIGNAL_REC data includes references to a callable PyObject and a + * PY_SIGNAL_SPEC_REC entry for the argument type list of 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, - * 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. Built-in signals in the sigmap are - * never removed; it is an error for the refcount of any such signal entry - * to drop to 0. + * If another script registers the same signal, the original entry's 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 or emit the signal untill it is + * re-registered. Built-in signals in the sigmap are not from the heap and are + * never removed; it is an error for the refcount of any such signal entry to + * drop to 0. */ typedef struct _PY_SIGNAL_SPEC_REC @@ -4,153 +4,127 @@ typedef struct _PY_SOURCE_REC { - int once; int tag; + GSList **tag_list; int fd; - GSList **container; /* "container" points to a list owned by a Script object */ - PyObject *handler; + PyObject *func; PyObject *data; } PY_SOURCE_REC; -static PY_SOURCE_REC *py_source_new(GSList **list, int once, PyObject *handler, PyObject *data) +static PY_SOURCE_REC *py_source_rec_new(GSList **tag_list, int fd, PyObject *func, PyObject *data) { PY_SOURCE_REC *rec; rec = g_new0(PY_SOURCE_REC, 1); - rec->once = once; - rec->fd = -1; - rec->handler = handler; + rec->tag_list = tag_list; + rec->fd = fd; + rec->func = func; rec->data = data; - rec->container = list; - Py_INCREF(handler); - Py_XINCREF(data); + Py_INCREF(func); + Py_XINCREF(data); return rec; } +static int py_remove_tag(GSList **list, int handle) +{ + GSList *node; + + node = g_slist_find(*list, GINT_TO_POINTER(handle)); + if (!node) + return 0; + + *list = g_slist_delete_link(*list, node); + + return 1; +} + static void py_source_destroy(PY_SOURCE_REC *rec) { - g_source_remove(rec->tag); - Py_DECREF(rec->handler); + g_return_if_fail(py_remove_tag(rec->tag_list, rec->tag) == 1); + Py_DECREF(rec->func); Py_XDECREF(rec->data); g_free(rec); } -static int py_source_proxy(PY_SOURCE_REC *rec) +static int py_handle_ret(PyObject *ret) { - char args[3] = {0,0,0}; - int fd; - PyObject *ret; - PyObject *handler, *data; - - /* Copy data out of the rec (there's not much). The rec may be deleted in - the if block below or by the Python code executed. Protect handler & data - with INCREF. - */ + int res; - fd = rec->fd; - handler = rec->handler; - data = rec->data; - Py_INCREF(handler); - Py_XINCREF(data); - - if (rec->once) + if (!ret) { - *rec->container = g_slist_remove(*rec->container, rec); - py_source_destroy(rec); + PyErr_Print(); + res = FALSE; + } + else + { + res = PyObject_IsTrue(ret); + Py_DECREF(ret); } - /* call python function with fd and/or data if available. - IO handler will be called with either "iO" or "i". - Timeout with "O" or "". - */ + return res; +} - if (fd >= 0) - { - /* IO handler */ - args[0] = 'i'; - if (data) - args[1] = 'O'; +static int py_timeout_proxy(PY_SOURCE_REC *rec) +{ + PyObject *ret; - ret = PyObject_CallFunction(handler, args, fd, data); - } + g_return_val_if_fail(rec != NULL, FALSE); + + if (rec->data) + ret = PyObject_CallFunction(rec->func, "O", rec->data); else - { - /* Timeout handler */ - if (data) - args[0] = 'O'; + ret = PyObject_CallFunction(rec->func, ""); - ret = PyObject_CallFunction(handler, args, data); - } + return py_handle_ret(ret); +} - if (!ret) - PyErr_Print(); +static int py_io_proxy(GIOChannel *src, GIOCondition condition, PY_SOURCE_REC *rec) +{ + PyObject *ret; + + g_return_val_if_fail(rec != NULL, FALSE); + + if (rec->data) + ret = PyObject_CallFunction(rec->func, "iiO", rec->fd, condition, rec->data); else - Py_DECREF(ret); + ret = PyObject_CallFunction(rec->func, "ii", rec->fd, condition); - Py_DECREF(handler); - Py_XDECREF(data); - - return 1; + return py_handle_ret(ret); } -int pysource_timeout_add_list(GSList **list, int msecs, PyObject *func, PyObject *data, int once) +int pysource_timeout_add_list(GSList **list, int msecs, PyObject *func, PyObject *data) { PY_SOURCE_REC *rec; g_return_val_if_fail(func != NULL, -1); - rec = py_source_new(list, once, func, data); - rec->tag = g_timeout_add(msecs, (GSourceFunc)py_source_proxy, rec); - - *list = g_slist_append(*list, rec); - + rec = py_source_rec_new(list, -1, func, data); + rec->tag = g_timeout_add_full(G_PRIORITY_DEFAULT, msecs, + (GSourceFunc)py_timeout_proxy, rec, + (GDestroyNotify)py_source_destroy); + + *list = g_slist_append(*list, GINT_TO_POINTER(rec->tag)); + return rec->tag; } -int pysource_input_add_list(GSList **list, int fd, int cond, PyObject *func, PyObject *data, int once) +int pysource_io_add_watch_list(GSList **list, int fd, int cond, PyObject *func, PyObject *data) { PY_SOURCE_REC *rec; GIOChannel *channel; g_return_val_if_fail(func != NULL, 1); - rec = py_source_new(list, once, func, data); - rec->fd = fd; + + rec = py_source_rec_new(list, fd, func, data); channel = g_io_channel_unix_new(fd); - rec->tag = g_input_add(channel, cond, (GInputFunction)py_source_proxy, rec); + rec->tag = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, cond, + (GIOFunc)py_io_proxy, rec, + (GDestroyNotify)py_source_destroy); g_io_channel_unref(channel); - *list = g_slist_append(*list, rec); + *list = g_slist_append(*list, GINT_TO_POINTER(rec->tag)); return rec->tag; } - -int pysource_remove_tag(GSList **list, int handle) -{ - GSList *node; - - for (node = *list; node != NULL; node = node->next) - { - PY_SOURCE_REC *rec = node->data; - - if (rec->tag == handle) - { - py_source_destroy(rec); - *list = g_slist_delete_link(*list, node); - - return 1; - } - } - - return 0; -} - -void pysource_remove_list(GSList *list) -{ - GSList *node; - - for (node = list; node != NULL; node = node->next) - py_source_destroy(node->data); -} - @@ -4,10 +4,7 @@ #include <Python.h> /* condition is G_INPUT_READ or G_INPUT_WRITE */ -int pysource_input_add_list(GSList **list, int fd, int cond, PyObject *func, PyObject *data, int once); -int pysource_timeout_add_list(GSList **list, int msecs, PyObject *func, PyObject *data, int once); - -int pysource_remove_tag(GSList **list, int handle); -void pysource_remove_list(GSList *list); +int pysource_io_add_watch_list(GSList **list, int fd, int cond, PyObject *func, PyObject *data); +int pysource_timeout_add_list(GSList **list, int msecs, PyObject *func, PyObject *data); #endif diff --git a/pythemes.c b/pythemes.c new file mode 100644 index 0000000..009c2ab --- /dev/null +++ b/pythemes.c @@ -0,0 +1,238 @@ +#include <Python.h> +#include "pythemes.h" +#include "pyirssi.h" +#include "factory.h" +#include "pymodule.h" +#include "pyloader.h" + +/* Edited from Perl Themes.xs */ +int pythemes_printformat(TEXT_DEST_REC *dest, const char *script, const char *format, PyObject *argtup) +{ + char *arglist[MAX_FORMAT_PARAMS + 1]; + THEME_REC *theme; + char *str; + int formatnum; + int i; + + formatnum = format_find_tag(script, format); + if (formatnum < 0) { + PyErr_Format(PyExc_KeyError, "unregistered format '%s'", format); + return 0; + } + + memset(arglist, 0, sizeof arglist); + for (i = 0; i < MAX_FORMAT_PARAMS && i < PyTuple_Size(argtup); i++) { + PyObject *obj = PyTuple_GET_ITEM(argtup, i); + char *str; + + if (!PyString_Check(obj)) { + PyErr_Format(PyExc_TypeError, "format argument list contains non-string data"); + return 0; + } + + str = PyString_AsString(obj); + if (!str) + return 0; + + arglist[i] = str; + } + + theme = window_get_theme(dest->window); + signal_emit("print format", 5, theme, script, + dest, GINT_TO_POINTER(formatnum), arglist); + + str = format_get_text_theme_charargs(theme, script, dest, formatnum, arglist); + if (*str != '\0') printtext_dest(dest, "%s", str); + g_free(str); + + return 1; +} + +static void py_destroy_format_list(FORMAT_REC *recs) +{ + int i; + + for (i = 0; recs[i].def; i++) + { + g_free(recs[i].def); + g_free(recs[i].tag); + } + + g_free(recs); +} + +/* register a list of formats in this format: + * [ (name, format), ... ] + */ +int pythemes_register(const char *script, PyObject *list) +{ + FORMAT_REC *formatrecs; + int i; + + if (!PyList_Check(list)) + { + PyErr_Format(PyExc_TypeError, "arg must be list"); + return 0; + } + + if (PyList_Size(list) == 0) + { + PyErr_Format(PyExc_TypeError, "cannot register empty list"); + return 0; + } + + if (g_hash_table_lookup(default_formats, script)) + { + PyErr_Format(PyExc_KeyError, "format list already registered by script"); + return 0; + } + + formatrecs = g_new0(FORMAT_REC, PyList_Size(list) + 2); + formatrecs[0].tag = g_strdup(script); + formatrecs[0].tag = g_strdup("Python script"); + + for (i = 0; i < PyList_Size(list); i++) + { + FORMAT_REC *rec; + PyObject *item; + char *key, *value; + + rec = &formatrecs[i + 1]; + item = PyList_GET_ITEM(list, i); + if (!PyArg_ParseTuple(item, "ss", &key, &value)) + { + if (PyErr_ExceptionMatches(PyExc_TypeError)) + { + PyErr_Clear(); + PyErr_Format(PyExc_TypeError, "format list must contain tuples of two strings"); + } + py_destroy_format_list(formatrecs); + return 0; + } + + rec->tag = g_strdup(key); + rec->def = g_strdup(value); + rec->params = MAX_FORMAT_PARAMS; + } + + theme_register_module(script, formatrecs); + + return 1; +} + +void pythemes_unregister(const char *script) +{ + FORMAT_REC *formats; + + formats = g_hash_table_lookup(default_formats, script); + if (!formats) + return; + + py_destroy_format_list(formats); + theme_unregister_module(script); +} + +PyDoc_STRVAR(py_printformat_doc, + "printformat(level, format, ...) -> None\n" +); +static PyObject *py_printformat(PyObject *self, PyObject *all) +{ + int level = 0; + char *format = ""; + PyObject *args = NULL, *varargs = NULL; + TEXT_DEST_REC dest; + char *script; + + args = PySequence_GetSlice(all, 0, 2); + if (!args) + goto error; + + varargs = PySequence_GetSlice(all, 2, PyTuple_Size(all)); + if (!varargs) + goto error; + + if (!PyArg_ParseTuple(args, "is", &level, &format)) + goto error; + + script = pyloader_find_script_name(); + if (!script) + { + PyErr_Format(PyExc_RuntimeError, "No script found"); + goto error; + } + + format_create_dest(&dest, NULL, NULL, level, NULL); + if (!pythemes_printformat(&dest, script, format, varargs)) + goto error; + + Py_DECREF(args); + Py_DECREF(varargs); + + Py_RETURN_NONE; + +error: + Py_XDECREF(args); + Py_XDECREF(varargs); + + return NULL; +} + +static int py_add_module_func(PyMethodDef *mdef) +{ + PyObject *func; + + g_return_val_if_fail(py_module != NULL, 0); + + func = PyCFunction_New(mdef, NULL); + if (!func) + return 0; + + if (PyModule_AddObject(py_module, mdef->ml_name, func) != 0) + { + Py_DECREF(func); + return 0; + } + + return 1; +} + +static int py_add_method(PyTypeObject *type, PyMethodDef *mdef) +{ + int ret; + PyObject *func; + + g_return_val_if_fail(type->tp_dict != NULL, 0); + + func = PyDescr_NewMethod(type, mdef); + if (!func) + return 0; + + ret = PyDict_SetItemString(type->tp_dict, mdef->ml_name, func); + Py_DECREF(func); + if (ret != 0) + return 0; + + return 1; +} + +int pythemes_init(void) +{ + static PyMethodDef pfdef = {"printformat", (PyCFunction)py_printformat, + METH_VARARGS, py_printformat_doc}; + + /* add function to main module and as member some types */ + + if (!py_add_module_func(&pfdef)) + return 0; + + if (!py_add_method(&PyServerType, &pfdef)) + return 0; + + if (!py_add_method(&PyWindowType, &pfdef)) + return 0; + + if (!py_add_method(&PyWindowItemType, &pfdef)) + return 0; + + return 1; +} diff --git a/pythemes.h b/pythemes.h new file mode 100644 index 0000000..92af965 --- /dev/null +++ b/pythemes.h @@ -0,0 +1,13 @@ +#ifndef _PYTHEMES_H_ +#define _PYTHEMES_H_ + +#include <Python.h> + +struct _TEXT_DEST_REC; + +int pythemes_printformat(struct _TEXT_DEST_REC *dest, const char *script, const char *format, PyObject *argtup); +int pythemes_register(const char *script, PyObject *list); +void pythemes_unregister(const char *script); +int pythemes_init(void); + +#endif |