diff options
author | Christopher Davis <loafier@gmail.com> | 2006-08-12 22:16:53 +0000 |
---|---|---|
committer | Christopher Davis <loafier@gmail.com> | 2006-08-12 22:16:53 +0000 |
commit | 3a028090359e5d5d24ccbfc11d9b6ff5681aab4f (patch) | |
tree | 0cfb8ec1eb8a49366fc663bef00bf4dfb1f7c307 /src | |
parent | f13ea25509e932d426ebd69d90368fe9b1d4c1ab (diff) | |
download | irssi-python-3a028090359e5d5d24ccbfc11d9b6ff5681aab4f.tar.gz irssi-python-3a028090359e5d5d24ccbfc11d9b6ff5681aab4f.tar.xz irssi-python-3a028090359e5d5d24ccbfc11d9b6ff5681aab4f.zip |
directory structure change
git-svn-id: http://svn.irssi.org/repos/irssi-python@4312 dbcabf3a-b0e7-0310-adc4-f8d773084564
Diffstat (limited to 'src')
96 files changed, 14991 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..123e0ed --- /dev/null +++ b/src/Makefile @@ -0,0 +1,35 @@ +CC = gcc + +PYTHON = /usr/include/python2.4 +IRSSI = /home/chrisd/irssi-0.8.10 + +CFLAGS = -fpic -ggdb -Wall -I$(PYTHON) -I$(IRSSI) -I$(IRSSI)/src \ +-I$(IRSSI)/src/fe-common/core -I$(IRSSI)/src/core -I$(IRSSI)/src/fe-text \ +-I$(IRSSI)/src/irc -I$(IRSSI)/src/irc/core -I$(IRSSI)/src/irc/dcc \ +-I$(IRSSI)/src/irc/notifylist -I.. -I. -Iobjects \ +`pkg-config glib-2.0 --cflags` + +LDFLAGS = -fpic /usr/lib/libpython2.4.so + +OBJ = pycore.o pyutils.o pymodule.o pyloader.o pysignals.o pysource.o \ +pythemes.o pystatusbar.o pyconstants.o + +pyirssi: pyobjects.a $(OBJ) + $(CC) -shared -o libirssi_python.so $(OBJ) objects/pyobjects.a $(LDFLAGS) + +pyobjects.a: + cd objects/ && make + +%.o: %.c + $(CC) -c $< $(CFLAGS) + +signalmap: + awk -f sig2code.awk ~/irssi-0.8.10/docs/signals.txt > pysigmap.h + +constants: + awk -f constants.awk constants.txt > pyconstants.c + +clean: + rm -f *.o *.so + cd objects/ && make clean + diff --git a/src/constants.awk b/src/constants.awk new file mode 100644 index 0000000..3aa5034 --- /dev/null +++ b/src/constants.awk @@ -0,0 +1,20 @@ +BEGIN { + print "#include \"pymodule.h\""; + print "#include \"pyirssi_irc.h\""; + print + print "void pyconstants_init(void)" + print "{" +} + +{ + if (NF >= 2) + constant = $2; + else + constant = $1; + + printf(" PyModule_AddIntConstant(py_module, \"%s\", %s);\n", $1, constant); +} + +END { + print "}" +} diff --git a/src/constants.txt b/src/constants.txt new file mode 100644 index 0000000..a3b8917 --- /dev/null +++ b/src/constants.txt @@ -0,0 +1,45 @@ +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 +IRSSI_GUI_NONE +IRSSI_GUI_QT +IRSSI_GUI_TEXT +IRC_MASK_DOMAIN +IRC_MASK_HOST +IRC_MASK_NICK +IRC_MASK_USER +MSGLEVEL_ACTIONS +MSGLEVEL_ALL +MSGLEVEL_CLIENTCRAP +MSGLEVEL_CLIENTERROR +MSGLEVEL_CLIENTNOTICE +MSGLEVEL_CRAP +MSGLEVEL_CTCPS +MSGLEVEL_DCC +MSGLEVEL_DCCMSGS +MSGLEVEL_HILIGHT +MSGLEVEL_INVITES +MSGLEVEL_JOINS +MSGLEVEL_KICKS +MSGLEVEL_LASTLOG +MSGLEVEL_MODES +MSGLEVEL_MSGS +MSGLEVEL_NEVER +MSGLEVEL_NICKS +MSGLEVEL_NO_ACT +MSGLEVEL_NOHILIGHT +MSGLEVEL_NOTICES +MSGLEVEL_PARTS +MSGLEVEL_PUBLIC +MSGLEVEL_QUITS +MSGLEVEL_SNOTES +MSGLEVEL_TOPICS +MSGLEVEL_WALLOPS +SIGNAL_PRIORITY_DEFAULT +SIGNAL_PRIORITY_HIGH +SIGNAL_PRIORITY_LOW diff --git a/src/irssi.py b/src/irssi.py new file mode 100644 index 0000000..3f193ad --- /dev/null +++ b/src/irssi.py @@ -0,0 +1,31 @@ +""" +""" + +import sys +import _irssi +from _irssi import * + +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/src/irssi_startup.py b/src/irssi_startup.py new file mode 100644 index 0000000..ee8dbdd --- /dev/null +++ b/src/irssi_startup.py @@ -0,0 +1,202 @@ +import sys, imp, __builtin__ +import _irssi + +class Output: + def __init__(self, level): + self.level = level + self.buf = [] + def write(self, text): + if not text: + return + self.buf.append(text) + if '\n' == text[-1]: + text = ''.join(self.buf)[:-1] + for line in text.split('\\n'): + _irssi.active_win().prnt(line, self.level) + self.buf = [] + +#XXX: hardcode +#try: +sys.stdout = Output(level = 0x0080000) +sys.stderr = Output(level = 0x0100000) +#except Exception, e: +# print 'Cant set output', e +# sys.stdout = sys.__stdout__ +# sys.stderr = sys.__stderr__ + +""" +#Import stuff modified from <Python>/Demo/imputil/knee.py +#If a script instance is available, it's module dictionary +#is used in place of sys.modules +#XXX: needs testing +#XXX: copyright? + +# Save the original hooks +original_import = __builtin__.__import__ +original_reload = __builtin__.reload + +# Replacement for __import__() +def import_hook(name, globals=None, locals=None, fromlist=None): + print 'LOADING', name + if name == 'sys': + return sys + #if name == '_irssi': + # return _irssi + + try: + script = _irssi.get_script() + except RuntimeError: + print 'ORIG IMPORT', name + return original_import(name, globals, locals, fromlist) + + parent = determine_parent(globals, script) + q, tail = find_head_package(parent, name, script) + m = load_tail(q, tail, script) + if not fromlist: + return q + + if hasattr(m, "__path__"): + ensure_fromlist(m, fromlist, script) + + recur -= 1 + return m + +def determine_parent(globals, script): + if not globals or not globals.has_key("__name__"): + print 'DP no __name__ in globals' + return None + pname = globals['__name__'] + print 'DP pname', pname + if globals.has_key("__path__"): + parent = script.modules[pname] + assert globals is parent.__dict__ + return parent + if '.' in pname: + i = pname.rfind('.') + pname = pname[:i] + parent = script.modules[pname] + assert parent.__name__ == pname + return parent + return None + +def find_head_package(parent, name, script): + if '.' in name: + i = name.find('.') + head = name[:i] + tail = name[i+1:] + else: + head = name + tail = "" + if parent: + qname = "%s.%s" % (parent.__name__, head) + else: + qname = head + q = import_module(head, qname, parent, script) + if q: + return q, tail + else: + print 'FHP (1) no q module for', name + + if parent: + qname = head + parent = None + q = import_module(head, qname, parent, script) + if q: + return q, tail + else: + print 'FHP (2) no q module for', name + + raise ImportError, "No module named " + qname + +def load_tail(q, tail, script): + m = q + while tail: + i = tail.find('.') + if i < 0: i = len(tail) + head, tail = tail[:i], tail[i+1:] + mname = "%s.%s" % (m.__name__, head) + m = import_module(head, mname, m, script) + if not m: + raise ImportError, "No module named " + mname + return m + +def ensure_fromlist(m, fromlist, script, recursive=0): + for sub in fromlist: + if sub == "*": + if not recursive: + try: + all = m.__all__ + except AttributeError: + pass + else: + assert recursive == 0 + ensure_fromlist(m, all, script, 1) + continue + if sub != "*" and not hasattr(m, sub): + subname = "%s.%s" % (m.__name__, sub) + submod = import_module(sub, subname, m, script) + if not submod: + raise ImportError, "No module named " + subname + +def import_module(partname, fqname, parent, script): + try: + m = script.modules[fqname] + #if hasattr(m, '_script'): + # assert m._script == script + + _irssi.prnt('LOADING CACHED %s script -> %s' % (fqname, repr(script))) + + return m + except KeyError: + print 'IM no cached moddule for', fqname + pass + + try: + fp, pathname, stuff = imp.find_module(partname, + parent and parent.__path__) + except ImportError, e: + print 'IM import error', e + return None + + try: + m = imp.load_module(fqname, fp, pathname, stuff) + finally: + if fp: fp.close() + if parent: + setattr(parent, partname, m) + + #don't load script into builtins, extensions, or this wrapper + if hasattr(m, '__file__') and fqname != 'irssi': + m._script = script + script.modules[fqname] = m + + #if hasattr(m, '__file__') and fqname not in ('__builtin__', 'sys', '__main__', '_irssi'): + # del sys.modules[fqname] + + _irssi.prnt('GOT -> %s, SCRIPT -> %s' % (m, repr(script))) + + return m + + +# Replacement for reload() +def reload_hook(module): + _irssi.prnt('reloading ' + repr(module)) + try: + script = _irssi.get_script() + except RuntimeError: + return original_reload(module) + + name = module.__name__ + if '.' not in name: + return import_module(name, name, None, script) + i = name.rfind('.') + pname = name[:i] + parent = script.modules[pname] + return import_module(name[i+1:], name, parent, script) + + +# Now install our hooks +__builtin__.__import__ = import_hook +__builtin__.reload = reload_hook +""" + diff --git a/src/objects/Makefile b/src/objects/Makefile new file mode 100644 index 0000000..6cc56fb --- /dev/null +++ b/src/objects/Makefile @@ -0,0 +1,28 @@ +CC = gcc + +PYTHON = /usr/include/python2.4 +IRSSI = /home/chrisd/irssi-0.8.10 +CFLAGS = -fpic -ggdb -Wall -I$(PYTHON) -I$(IRSSI) -I$(IRSSI)/src \ +-I$(IRSSI)/src/fe-common/core -I$(IRSSI)/src/core -I$(IRSSI)/src/fe-text \ +-I$(IRSSI)/src/irc -I$(IRSSI)/src/irc/core -I$(IRSSI)/src/irc/dcc \ +-I$(IRSSI)/src/irc/notifylist -I.. \ +`pkg-config glib-2.0 --cflags` + +OBJ = pyscript-object.o base-objects.o window-item-object.o channel-object.o \ +query-object.o server-object.o connect-object.o irc-server-object.o \ +irc-connect-object.o irc-channel-object.o ban-object.o nick-object.o \ +chatnet-object.o reconnect-object.o window-object.o textdest-object.o \ +rawlog-object.o log-object.o logitem-object.o ignore-object.o \ +dcc-object.o dcc-chat-object.o dcc-get-object.o dcc-send-object.o \ +netsplit-object.o netsplit-server-object.o netsplit-channel-object.o \ +notifylist-object.o process-object.o command-object.o theme-object.o \ +statusbar-item-object.o main-window-object.o factory.o + +pyobjects.a: $(OBJ) + ar r pyobjects.a $(OBJ) + +%.o: %.c + $(CC) -c $< $(CFLAGS) + +clean: + rm -f *.o *.so *.a diff --git a/src/objects/ban-object.c b/src/objects/ban-object.c new file mode 100644 index 0000000..908bae2 --- /dev/null +++ b/src/objects/ban-object.c @@ -0,0 +1,155 @@ +#include <Python.h> +#include "pyirssi_irc.h" +#include "pymodule.h" +#include "ban-object.h" +#include "pycore.h" + +/* monitor "ban remove" signal */ +static void ban_cleanup(CHANNEL_REC *chan, BAN_REC *ban) +{ + PyBan *pyban = signal_get_user_data(); + + if (ban == pyban->data) + { + pyban->data = NULL; + pyban->cleanup_installed = 0; + signal_remove_data("ban remove", ban_cleanup, pyban); + } +} + +static void PyBan_dealloc(PyBan *self) +{ + if (self->cleanup_installed) + signal_remove_data("ban remove", ban_cleanup, self); + + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyBan_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyBan *self; + + self = (PyBan *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +PyDoc_STRVAR(PyBan_ban_doc, + "The ban" +); +static PyObject *PyBan_ban_get(PyBan *self, void *closure) +{ + BAN_REC *data = self->data; + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(data->ban); +} + +PyDoc_STRVAR(PyBan_setby_doc, + "Nick of who set the ban" +); +static PyObject *PyBan_setby_get(PyBan *self, void *closure) +{ + BAN_REC *data = self->data; + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(data->setby); +} + +PyDoc_STRVAR(PyBan_time_doc, + "Timestamp when ban was set" +); +static PyObject *PyBan_time_get(PyBan *self, void *closure) +{ + BAN_REC *data = self->data; + RET_NULL_IF_INVALID(self->data); + return PyLong_FromUnsignedLong(data->time); +} + +/* specialized getters/setters */ +static PyGetSetDef PyBan_getseters[] = { + {"ban", (getter)PyBan_ban_get, NULL, + PyBan_ban_doc, NULL}, + {"setby", (getter)PyBan_setby_get, NULL, + PyBan_setby_doc, NULL}, + {"time", (getter)PyBan_time_get, NULL, + PyBan_time_doc, NULL}, + {NULL} +}; + +/* Methods for object */ +static PyMethodDef PyBan_methods[] = { + {NULL} /* Sentinel */ +}; + +PyTypeObject PyBanType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Ban", /*tp_name*/ + sizeof(PyBan), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyBan_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyBan objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyBan_methods, /* tp_methods */ + 0, /* tp_members */ + PyBan_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyBan_new, /* tp_new */ +}; + + +/* window item wrapper factory function */ +PyObject *pyban_new(void *ban) +{ + PyBan *pyban; + + pyban = py_inst(PyBan, PyBanType); + if (!pyban) + return NULL; + + pyban->data = ban; + pyban->cleanup_installed = 1; + signal_add_last_data("ban remove", ban_cleanup, pyban); + + return (PyObject *)pyban; +} + +int ban_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyBanType) < 0) + return 0; + + Py_INCREF(&PyBanType); + PyModule_AddObject(py_module, "Ban", (PyObject *)&PyBanType); + + return 1; +} diff --git a/src/objects/ban-object.h b/src/objects/ban-object.h new file mode 100644 index 0000000..59ec9e7 --- /dev/null +++ b/src/objects/ban-object.h @@ -0,0 +1,18 @@ +#ifndef _BAN_OBJECT_H_ +#define _BAN_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +typedef struct +{ + PyIrssiFinal_HEAD(void) +} PyBan; + +extern PyTypeObject PyBanType; + +int ban_object_init(void); +PyObject *pyban_new(void *ban); +#define pyban_check(op) PyObject_TypeCheck(op, &PyBanType) + +#endif diff --git a/src/objects/base-objects.c b/src/objects/base-objects.c new file mode 100644 index 0000000..42f5cac --- /dev/null +++ b/src/objects/base-objects.c @@ -0,0 +1,235 @@ +#include <Python.h> +#include "structmember.h" +#include "pymodule.h" +#include "base-objects.h" +#include "pyirssi.h" + +/* This is the base type for Irssi objects with a type id. The user can find + * the type name, type id, and check if the object is wrapping a valid Irssi + * record. + */ + +static void PyIrssiBase_dealloc(PyIrssiBase *self) +{ + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyIrssiBase_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyIrssiBase *self; + + self = (PyIrssiBase *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* Getters */ +PyDoc_STRVAR(PyIrssiBase_type_id_doc, + "Irssi's type id for object" +); +static PyObject *PyIrssiBase_type_id_get(PyIrssiBase *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->type); +} + +PyDoc_STRVAR(PyIrssiBase_type_doc, + "Irssi's name for object" +); +static PyObject *PyIrssiBase_type_get(PyIrssiBase *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->base_name); +} + +PyDoc_STRVAR(PyIrssiBase_valid_doc, + "True if the object is valid" +); +static PyObject *PyIrssiBase_valid_get(PyIrssiBase *self, void *closure) +{ + if (self->data != NULL) + Py_RETURN_TRUE; + + Py_RETURN_FALSE; +} + +/* specialized getters/setters */ +static PyGetSetDef PyIrssiBase_getseters[] = { + {"type_id", (getter)PyIrssiBase_type_id_get, NULL, + PyIrssiBase_type_id_doc, NULL}, + {"type", (getter)PyIrssiBase_type_get, NULL, + PyIrssiBase_type_doc, NULL}, + {"valid", (getter)PyIrssiBase_valid_get, NULL, + PyIrssiBase_valid_doc, NULL}, + {NULL} +}; + +/* Methods for object */ +static PyMethodDef PyIrssiBase_methods[] = { + {NULL} +}; + +PyTypeObject PyIrssiBaseType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "IrssiBase", /*tp_name*/ + sizeof(PyIrssiBase), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyIrssiBase_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyIrssiBase objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyIrssiBase_methods, /* tp_methods */ + 0, /* tp_members */ + PyIrssiBase_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyIrssiBase_new, /* tp_new */ +}; + + +/* IrssiChatBase is a base type for any object with a chat type. The user + can find the chat type string name with the chat_type member or + the type id with the chat_type_id member. It inherits from IrssiBase + so the type, valid, and type_id members are visible to the user, too */ + +static void PyIrssiChatBase_dealloc(PyIrssiChatBase *self) +{ + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyIrssiChatBase_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyIrssiChatBase *self; + + self = (PyIrssiChatBase *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* Getters */ +PyDoc_STRVAR(PyIrssiChatBase_chat_type_id_doc, + "Chat Type id (int)" +); +static PyObject *PyIrssiChatBase_chat_type_id_get(PyIrssiChatBase *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->chat_type); +} + +PyDoc_STRVAR(PyIrssiChatBase_chat_type_doc, + "Chat name (str)" +); +static PyObject *PyIrssiChatBase_chat_type_get(PyIrssiChatBase *self, void *closure) +{ + CHAT_PROTOCOL_REC *rec; + + RET_NULL_IF_INVALID(self->data); + + rec = chat_protocol_find_id(self->data->chat_type); + if (rec) + RET_AS_STRING_OR_NONE(rec->name); + else + Py_RETURN_NONE; +} + +/* specialized getters/setters */ +static PyGetSetDef PyIrssiChatBase_getseters[] = { + {"chat_type_id", (getter)PyIrssiChatBase_chat_type_id_get, NULL, + PyIrssiChatBase_chat_type_id_doc, NULL}, + {"chat_type", (getter)PyIrssiChatBase_chat_type_get, NULL, + PyIrssiChatBase_chat_type_doc, NULL}, + {NULL} +}; + +/* Methods */ +static PyMethodDef PyIrssiChatBase_methods[] = { + {NULL} +}; + +PyTypeObject PyIrssiChatBaseType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "IrssiChatBase", /*tp_name*/ + sizeof(PyIrssiChatBase), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyIrssiChatBase_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyIrssiChatBase objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyIrssiChatBase_methods, /* tp_methods */ + 0, /* tp_members */ + PyIrssiChatBase_getseters, /* tp_getset */ + &PyIrssiBaseType, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyIrssiChatBase_new, /* tp_new */ +}; + +int base_objects_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyIrssiBaseType) < 0) + return 0; + if (PyType_Ready(&PyIrssiChatBaseType) < 0) + return 0; + + Py_INCREF(&PyIrssiBaseType); + Py_INCREF(&PyIrssiChatBaseType); + PyModule_AddObject(py_module, "IrssiBase", (PyObject *)&PyIrssiBaseType); + PyModule_AddObject(py_module, "IrssiChatBase", (PyObject *)&PyIrssiChatBaseType); + + return 1; +} diff --git a/src/objects/base-objects.h b/src/objects/base-objects.h new file mode 100644 index 0000000..5351ead --- /dev/null +++ b/src/objects/base-objects.h @@ -0,0 +1,105 @@ +#ifndef _BASE_OBJECTS_H_ +#define _BASE_OBJECTS_H_ + +#include <Python.h> + +/* data is a pointer to the underlying Irssi record */ +/* base_name is the type name of the object returned from the type member + it can be SERVER, CHANNEL, QUERY, etc. Note: base_name isn't freed, so + it cannot point to heap memory */ +/* cleanup_installed: 1 = a cleanup signal handler is installed, 0 = not installed */ +#define PyIrssi_HEAD(type) \ + PyObject_HEAD \ + type *data; \ + const char *base_name; \ + int cleanup_installed; + +/* for uninheritable objects without a type id (Ban, Log, etc) */ +#define PyIrssiFinal_HEAD(type) \ + PyObject_HEAD \ + type *data; \ + int cleanup_installed; + +/* to access data from any irssi object */ +typedef struct +{ + PyObject_HEAD + void *data; +} PyIrssiObject; + +#define DATA(obj) (obj? ((PyIrssiObject *)obj)->data : NULL) + +/* base for classes with a type */ +typedef struct +{ + int type; +} IRSSI_BASE_REC; + +typedef struct +{ + PyIrssi_HEAD(IRSSI_BASE_REC) +} PyIrssiBase; + + +/* base for classes with type and a chat type */ +typedef struct +{ + int type; + int chat_type; +} IRSSI_CHAT_REC; + +typedef struct +{ + PyIrssi_HEAD(IRSSI_CHAT_REC) +} PyIrssiChatBase; + +extern PyTypeObject PyIrssiBaseType; +extern PyTypeObject PyIrssiChatBaseType; + +#define pybase_check(op) PyObject_TypeCheck(op, &PyIrssiBaseType) +#define pychatbase_check(op) PyObject_TypeCheck(op, &PyIrssiChatBaseType) +#define py_instp(tp, to) ((tp *) (to)->tp_alloc(to, 0)) +#define py_inst(tp, to) py_instp(tp, &to) + +int base_objects_init(void); + +#define RET_NULL_IF_INVALID(data) \ + if (data == NULL) \ + return PyErr_Format(PyExc_RuntimeError, "wrapped object is invalid") + +#define RET_N1_IF_INVALID(data) \ +do { \ + if (data == NULL) \ + { \ + PyErr_Format(PyExc_RuntimeError, "wrapped object is invalid"); \ + return -1; \ + } \ +} while (0) + +#define RET_AS_STRING_OR_NONE(str) \ +do { \ + if (str) \ + return PyString_FromString(str); \ + else \ + { \ + Py_INCREF(Py_None); \ + return Py_None; \ + } \ +} while (0) + + +#define RET_AS_STRING_OR_EMPTY(str) return PyString_FromString(str? str : "") + +#define RET_AS_OBJ_OR_NONE(obj) \ +do { \ + PyObject *tmp = obj; \ + if (!tmp) \ + tmp = Py_None; \ + Py_INCREF(tmp); \ + return tmp; \ +} while (0) + +#define INVALID_MEMBER(member) \ + return PyErr_Format(PyExc_RuntimeError, "invalid member id, %d", member) + +#endif diff --git a/src/objects/channel-object.c b/src/objects/channel-object.c new file mode 100644 index 0000000..fa4edd4 --- /dev/null +++ b/src/objects/channel-object.c @@ -0,0 +1,379 @@ +#include <Python.h> +#include "pyirssi.h" +#include "pymodule.h" +#include "factory.h" +#include "channel-object.h" +#include "pycore.h" + +/* monitor "channel destroyed" signal */ +static void chan_cleanup(CHANNEL_REC *chan) +{ + PyChannel *pychan = signal_get_user_data(); + + if (chan == pychan->data) + { + pychan->data = NULL; + pychan->cleanup_installed = 0; + signal_remove_data("channel destroyed", chan_cleanup, pychan); + } +} + +static void PyChannel_dealloc(PyChannel *self) +{ + if (self->cleanup_installed) + signal_remove_data("channel destroyed", chan_cleanup, self); + + self->ob_type->tp_free((PyObject*)self); +} + +/* Getters */ +PyDoc_STRVAR(PyChannel_topic_doc, + "Channel topic" +); +static PyObject *PyChannel_topic_get(PyChannel *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->topic); +} + +PyDoc_STRVAR(PyChannel_topic_by_doc, + "Nick who set the topic" +); +static PyObject *PyChannel_topic_by_get(PyChannel *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->topic_by); +} + +PyDoc_STRVAR(PyChannel_topic_time_doc, + "Timestamp when the topic was set" +); +static PyObject *PyChannel_topic_time_get(PyChannel *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromLong(self->data->topic_time); +} + +PyDoc_STRVAR(PyChannel_no_modes_doc, + "Channel is modeless" +); +static PyObject *PyChannel_no_modes_get(PyChannel *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->no_modes); +} + +PyDoc_STRVAR(PyChannel_mode_doc, + "Channel mode" +); +static PyObject *PyChannel_mode_get(PyChannel *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->mode); +} + +PyDoc_STRVAR(PyChannel_limit_doc, + "Max. users in channel (+l mode)" +); +static PyObject *PyChannel_limit_get(PyChannel *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->limit); +} + +PyDoc_STRVAR(PyChannel_key_doc, + "Channel key (password)" +); +static PyObject *PyChannel_key_get(PyChannel *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->key); +} + +PyDoc_STRVAR(PyChannel_chanop_doc, + "You are channel operator" +); +static PyObject *PyChannel_chanop_get(PyChannel *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->chanop); +} + +PyDoc_STRVAR(PyChannel_names_got_doc, + "/NAMES list has been received" +); +static PyObject *PyChannel_names_got_get(PyChannel *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->names_got); +} + +PyDoc_STRVAR(PyChannel_wholist_doc, + "/WHO list has been received" +); +static PyObject *PyChannel_wholist_get(PyChannel *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->wholist); +} + +PyDoc_STRVAR(PyChannel_synced_doc, + "Channel is fully synchronized" +); +static PyObject *PyChannel_synced_get(PyChannel *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->synced); +} + +PyDoc_STRVAR(PyChannel_joined_doc, + "JOIN event for this channel has been received" +); +static PyObject *PyChannel_joined_get(PyChannel *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->joined); +} + +PyDoc_STRVAR(PyChannel_left_doc, + "You just left the channel (for 'channel destroyed' event)" +); +static PyObject *PyChannel_left_get(PyChannel *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->left); +} + +PyDoc_STRVAR(PyChannel_kicked_doc, + "You were just kicked out of the channel (for 'channel destroyed' event)" +); +static PyObject *PyChannel_kicked_get(PyChannel *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->kicked); +} + +/* specialized getters/setters */ +static PyGetSetDef PyChannel_getseters[] = { + {"topic", (getter)PyChannel_topic_get, NULL, + PyChannel_topic_doc, NULL}, + {"topic_by", (getter)PyChannel_topic_by_get, NULL, + PyChannel_topic_by_doc, NULL}, + {"topic_time", (getter)PyChannel_topic_time_get, NULL, + PyChannel_topic_time_doc, NULL}, + {"no_modes", (getter)PyChannel_no_modes_get, NULL, + PyChannel_no_modes_doc, NULL}, + {"mode", (getter)PyChannel_mode_get, NULL, + PyChannel_mode_doc, NULL}, + {"limit", (getter)PyChannel_limit_get, NULL, + PyChannel_limit_doc, NULL}, + {"key", (getter)PyChannel_key_get, NULL, + PyChannel_key_doc, NULL}, + {"chanop", (getter)PyChannel_chanop_get, NULL, + PyChannel_chanop_doc, NULL}, + {"names_got", (getter)PyChannel_names_got_get, NULL, + PyChannel_names_got_doc, NULL}, + {"wholist", (getter)PyChannel_wholist_get, NULL, + PyChannel_wholist_doc, NULL}, + {"synced", (getter)PyChannel_synced_get, NULL, + PyChannel_synced_doc, NULL}, + {"joined", (getter)PyChannel_joined_get, NULL, + PyChannel_joined_doc, NULL}, + {"left", (getter)PyChannel_left_get, NULL, + PyChannel_left_doc, NULL}, + {"kicked", (getter)PyChannel_kicked_get, NULL, + PyChannel_kicked_doc, NULL}, + {NULL} +}; + +/* Methods */ +PyDoc_STRVAR(PyChannel_nicks_doc, + "nicks() -> list of Nick objects\n" + "\n" + "Return a list of nicks in the channel.\n" +); +static PyObject *PyChannel_nicks(PyChannel *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + return py_irssi_chatlist_new(nicklist_getnicks(self->data), 1); +} + +PyDoc_STRVAR(PyChannel_nicks_find_mask_doc, + "nicks_find_mask(mask) -> Nick object or None\n" + "\n" + "Find nick mask from nicklist, wildcards allowed.\n" +); +static PyObject *PyChannel_nicks_find_mask(PyChannel *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"mask", NULL}; + char *mask = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &mask)) + return NULL; + + return py_irssi_chat_new(nicklist_find_mask(self->data, mask), 1); +} + +PyDoc_STRVAR(PyChannel_nick_find_doc, + "nick_find(nick) -> Nick object or None\n" + "\n" + "Find nick from nicklist.\n" +); +static PyObject *PyChannel_nick_find(PyChannel *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"nick", NULL}; + char *nick = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &nick)) + return NULL; + + return py_irssi_chat_new(nicklist_find(self->data, nick), 1); +} + +PyDoc_STRVAR(PyChannel_nick_remove_doc, + "nick_remove(nick) -> None\n" + "\n" + "Remove nick from nicklist.\n" +); +static PyObject *PyChannel_nick_remove(PyChannel *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"nick", NULL}; + PyObject *nick = NULL; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, + &nick)) + return NULL; + + if (!pynick_check(nick)) + return PyErr_Format(PyExc_TypeError, "arg must be nick"); + + nicklist_remove(self->data, ((PyNick*)nick)->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyChannel_nick_insert_obj_doc, + "nick_insert(nick) -> None\n" + "\n" + "Insert nick object into nicklist.\n" +); +static PyObject *PyChannel_nick_insert_obj(PyChannel *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"nick", NULL}; + PyObject *nick = NULL; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, + &nick)) + return NULL; + + if (!pynick_check(nick)) + return PyErr_Format(PyExc_TypeError, "arg must be nick"); + + nicklist_insert(self->data, ((PyNick*)nick)->data); + + Py_RETURN_NONE; +} + +/* Methods for object */ +static PyMethodDef PyChannel_methods[] = { + {"nicks", (PyCFunction)PyChannel_nicks, METH_NOARGS, + PyChannel_nicks_doc}, + {"nicks_find_mask", (PyCFunction)PyChannel_nicks_find_mask, METH_VARARGS | METH_KEYWORDS, + PyChannel_nicks_find_mask_doc}, + {"nick_find", (PyCFunction)PyChannel_nick_find, METH_VARARGS | METH_KEYWORDS, + PyChannel_nick_find_doc}, + {"nick_remove", (PyCFunction)PyChannel_nick_remove, METH_VARARGS | METH_KEYWORDS, + PyChannel_nick_remove_doc}, + {"nick_insert_obj", (PyCFunction)PyChannel_nick_insert_obj, METH_VARARGS | METH_KEYWORDS, + PyChannel_nick_insert_obj_doc}, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyChannelType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Channel", /*tp_name*/ + sizeof(PyChannel), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyChannel_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyChannel objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyChannel_methods, /* tp_methods */ + 0, /* tp_members */ + PyChannel_getseters, /* tp_getset */ + &PyWindowItemType, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + + +/* window item wrapper factory function */ +PyObject *pychannel_sub_new(void *chan, const char *name, PyTypeObject *type) +{ + PyObject *pychan; + + pychan = pywindow_item_sub_new(chan, name, type); + if (pychan) + { + PyChannel *pych = (PyChannel *)pychan; + signal_add_last_data("channel destroyed", chan_cleanup, pych); + pych->cleanup_installed = 1; + } + + return pychan; +} + +PyObject *pychannel_new(void *chan) +{ + static const char *name = "CHANNEL"; + return pychannel_sub_new(chan, name, &PyChannelType); +} + +int channel_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyChannelType) < 0) + return 0; + + Py_INCREF(&PyChannelType); + PyModule_AddObject(py_module, "Channel", (PyObject *)&PyChannelType); + + return 1; +} diff --git a/src/objects/channel-object.h b/src/objects/channel-object.h new file mode 100644 index 0000000..0858e7b --- /dev/null +++ b/src/objects/channel-object.h @@ -0,0 +1,22 @@ +#ifndef _CHANNEL_OBJECT_H_ +#define _CHANNEL_OBJECT_H_ + +#include <Python.h> +#include "window-item-object.h" + +/* forward */ +struct _CHANNEL_REC; + +typedef struct +{ + PyWindowItem_HEAD(struct _CHANNEL_REC) +} PyChannel; + +extern PyTypeObject PyChannelType; + +int channel_object_init(void); +PyObject *pychannel_sub_new(void *chan, const char *name, PyTypeObject *type); +PyObject *pychannel_new(void *chan); +#define pychannel_check(op) PyObject_TypeCheck(op, &PyChannelType) + +#endif diff --git a/src/objects/chatnet-object.c b/src/objects/chatnet-object.c new file mode 100644 index 0000000..c22f0cb --- /dev/null +++ b/src/objects/chatnet-object.c @@ -0,0 +1,182 @@ +#include <Python.h> +#include "pymodule.h" +#include "base-objects.h" +#include "chatnet-object.h" +#include "pyirssi.h" +#include "pycore.h" +#include "pyutils.h" + +static void chatnet_cleanup(CHATNET_REC *cn) +{ + PyChatnet *pycn = signal_get_user_data(); + + if (cn == pycn->data) + { + pycn->data = NULL; + pycn->cleanup_installed = 0; + signal_remove_data("chatnet destroyed", chatnet_cleanup, pycn); + } +} + +static void PyChatnet_dealloc(PyChatnet *self) +{ + if (self->cleanup_installed) + signal_remove_data("chatnet destroyed", chatnet_cleanup, self); + + self->ob_type->tp_free((PyObject*)self); +} + +/* Getters */ +PyDoc_STRVAR(PyChatnet_name_doc, + "name of chat network" +); +static PyObject *PyChatnet_name_get(PyChatnet *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->name); +} + +PyDoc_STRVAR(PyChatnet_nick_doc, + "if not empty, nick preferred in this network" +); +static PyObject *PyChatnet_nick_get(PyChatnet *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->nick); +} + +PyDoc_STRVAR(PyChatnet_username_doc, + "if not empty, username preferred in this network" +); +static PyObject *PyChatnet_username_get(PyChatnet *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->username); +} + +PyDoc_STRVAR(PyChatnet_realname_doc, + "if not empty, realname preferred in this network" +); +static PyObject *PyChatnet_realname_get(PyChatnet *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->realname); +} + +PyDoc_STRVAR(PyChatnet_own_host_doc, + "address to use when connecting to this network" +); +static PyObject *PyChatnet_own_host_get(PyChatnet *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->own_host); +} + +PyDoc_STRVAR(PyChatnet_autosendcmd_doc, + "command to send after connecting to this network" +); +static PyObject *PyChatnet_autosendcmd_get(PyChatnet *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->autosendcmd); +} + +/* specialized getters/setters */ +static PyGetSetDef PyChatnet_getseters[] = { + {"name", (getter)PyChatnet_name_get, NULL, + PyChatnet_name_doc, NULL}, + {"nick", (getter)PyChatnet_nick_get, NULL, + PyChatnet_nick_doc, NULL}, + {"username", (getter)PyChatnet_username_get, NULL, + PyChatnet_username_doc, NULL}, + {"realname", (getter)PyChatnet_realname_get, NULL, + PyChatnet_realname_doc, NULL}, + {"own_host", (getter)PyChatnet_own_host_get, NULL, + PyChatnet_own_host_doc, NULL}, + {"autosendcmd", (getter)PyChatnet_autosendcmd_get, NULL, + PyChatnet_autosendcmd_doc, NULL}, + {NULL} +}; + +static PyMethodDef PyChatnet_methods[] = { + {NULL} /* Sentinel */ +}; + +PyTypeObject PyChatnetType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Chatnet", /*tp_name*/ + sizeof(PyChatnet), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyChatnet_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyChatnet objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyChatnet_methods, /* tp_methods */ + 0, /* tp_members */ + PyChatnet_getseters, /* tp_getset */ + &PyIrssiChatBaseType, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + + +/* chatnet factory function */ +PyObject *pychatnet_sub_new(void *cn, PyTypeObject *subclass) +{ + static const char *name = "CHATNET"; + PyChatnet *pycn = NULL; + + pycn = py_instp(PyChatnet, subclass); + if (!pycn) + return NULL; + + pycn->data = cn; + pycn->base_name = name; + signal_add_last_data("chatnet destroyed", chatnet_cleanup, pycn); + pycn->cleanup_installed = 1; + + return (PyObject *)pycn; +} + +PyObject *pychatnet_new(void *cn) +{ + return pychatnet_sub_new(cn, &PyChatnetType); +} + +int chatnet_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyChatnetType) < 0) + return 0; + + Py_INCREF(&PyChatnetType); + PyModule_AddObject(py_module, "Chatnet", (PyObject *)&PyChatnetType); + + return 1; +} diff --git a/src/objects/chatnet-object.h b/src/objects/chatnet-object.h new file mode 100644 index 0000000..8a6d1fc --- /dev/null +++ b/src/objects/chatnet-object.h @@ -0,0 +1,22 @@ +#ifndef _CHATNET_OBJECT_H_ +#define _CHATNET_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +/* forward */ +struct _CHATNET_REC; + +typedef struct +{ + PyIrssi_HEAD(struct _CHATNET_REC) +} PyChatnet; + +extern PyTypeObject PyChatnetType; + +int chatnet_object_init(void); +PyObject *pychatnet_sub_new(void *cn, PyTypeObject *subclass); +PyObject *pychatnet_new(void *cn); +#define pychatnet_check(op) PyObject_TypeCheck(op, &PyChatnetType) + +#endif diff --git a/src/objects/command-object.c b/src/objects/command-object.c new file mode 100644 index 0000000..ad60012 --- /dev/null +++ b/src/objects/command-object.c @@ -0,0 +1,145 @@ +#include <Python.h> +#include "pyirssi.h" +#include "pymodule.h" +#include "command-object.h" +#include "pycore.h" + +#define COMMAND(cmd) ((COMMAND_REC *)cmd) + +/* monitor "commandlist remove" signal */ +static void command_cleanup(COMMAND_REC *command) +{ + PyCommand *pycommand = signal_get_user_data(); + + if (command == pycommand->data) + { + pycommand->data = NULL; + pycommand->cleanup_installed = 0; + signal_remove_data("commandlist remove", command_cleanup, pycommand); + } +} + +static void PyCommand_dealloc(PyCommand *self) +{ + if (self->cleanup_installed) + signal_remove_data("commandlist remove", command_cleanup, self); + + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyCommand_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyCommand *self; + + self = (PyCommand *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* Getters */ +PyDoc_STRVAR(PyCommand_cmd_doc, + "Command name" +); +static PyObject *PyCommand_cmd_get(PyCommand *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(COMMAND(self->data)->cmd); +} + +PyDoc_STRVAR(PyCommand_category_doc, + "Category" +); +static PyObject *PyCommand_category_get(PyCommand *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(COMMAND(self->data)->category); +} + +/* specialized getters/setters */ +static PyGetSetDef PyCommand_getseters[] = { + {"cmd", (getter)PyCommand_cmd_get, NULL, + PyCommand_cmd_doc, NULL}, + {"category", (getter)PyCommand_category_get, NULL, + PyCommand_category_doc, NULL}, + {NULL} +}; + +/* Methods */ +/* Methods for object */ +static PyMethodDef PyCommand_methods[] = { + {NULL} /* Sentinel */ +}; + +PyTypeObject PyCommandType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Command", /*tp_name*/ + sizeof(PyCommand), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyCommand_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyCommand objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyCommand_methods, /* tp_methods */ + 0, /* tp_members */ + PyCommand_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyCommand_new, /* tp_new */ +}; + + +/* command factory function */ +PyObject *pycommand_new(void *command) +{ + PyCommand *pycommand; + + pycommand = py_inst(PyCommand, PyCommandType); + if (!pycommand) + return NULL; + + pycommand->data = command; + pycommand->cleanup_installed = 1; + signal_add_last_data("commandlist remove", command_cleanup, pycommand); + + return (PyObject *)pycommand; +} + +int command_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyCommandType) < 0) + return 0; + + Py_INCREF(&PyCommandType); + PyModule_AddObject(py_module, "Command", (PyObject *)&PyCommandType); + + return 1; +} diff --git a/src/objects/command-object.h b/src/objects/command-object.h new file mode 100644 index 0000000..e6402cf --- /dev/null +++ b/src/objects/command-object.h @@ -0,0 +1,18 @@ +#ifndef _COMMAND_OBJECT_H_ +#define _COMMAND_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +typedef struct +{ + PyIrssiFinal_HEAD(void) +} PyCommand; + +extern PyTypeObject PyCommandType; + +int command_object_init(void); +PyObject *pycommand_new(void *command); +#define pycommand_check(op) PyObject_TypeCheck(op, &PyCommandType) + +#endif diff --git a/src/objects/connect-object.c b/src/objects/connect-object.c new file mode 100644 index 0000000..8ef9a29 --- /dev/null +++ b/src/objects/connect-object.c @@ -0,0 +1,198 @@ +#include <Python.h> +#include "pymodule.h" +#include "base-objects.h" +#include "connect-object.h" +#include "pyirssi.h" +#include "pycore.h" +#include "pyutils.h" + +static void connect_cleanup(SERVER_CONNECT_REC *connect) +{ + PyConnect *pyconn = signal_get_user_data(); + + /* + if (server == pyconn->data) + { + pyserver->data = NULL; + pyserver->cleanup_installed = 0; + signal_remove_data("server disconnected", connect_cleanup, pyserver); + } + */ +} + +static void PyConnect_dealloc(PyConnect *self) +{ + if (self->cleanup_installed) + signal_remove_data("server disconnected", connect_cleanup, self); + + self->ob_type->tp_free((PyObject*)self); +} + +/* Getters */ +PyDoc_STRVAR(PyConnect_address_doc, + "Address where we connected (irc.blah.org)" +); +static PyObject *PyConnect_address_get(PyConnect *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->address); +} + +PyDoc_STRVAR(PyConnect_port_doc, + "Port where we're connected" +); +static PyObject *PyConnect_port_get(PyConnect *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->port); +} + +PyDoc_STRVAR(PyConnect_chatnet_doc, + "Chat network" +); +static PyObject *PyConnect_chatnet_get(PyConnect *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->chatnet); +} + +PyDoc_STRVAR(PyConnect_password_doc, + "Password we used in connection." +); +static PyObject *PyConnect_password_get(PyConnect *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->password); +} + +PyDoc_STRVAR(PyConnect_wanted_nick_doc, + "Nick which we would prefer to use" +); +static PyObject *PyConnect_wanted_nick_get(PyConnect *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->nick); +} + +PyDoc_STRVAR(PyConnect_username_doc, + "User name" +); +static PyObject *PyConnect_username_get(PyConnect *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->username); +} + +PyDoc_STRVAR(PyConnect_realname_doc, + "Real name" +); +static PyObject *PyConnect_realname_get(PyConnect *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->realname); +} + +/* Get/Set */ +static PyGetSetDef PyConnect_getseters[] = { + {"address", (getter)PyConnect_address_get, NULL, + PyConnect_address_doc, NULL}, + {"port", (getter)PyConnect_port_get, NULL, + PyConnect_port_doc, NULL}, + {"chatnet", (getter)PyConnect_chatnet_get, NULL, + PyConnect_chatnet_doc, NULL}, + {"password", (getter)PyConnect_password_get, NULL, + PyConnect_password_doc, NULL}, + {"wanted_nick", (getter)PyConnect_wanted_nick_get, NULL, + PyConnect_wanted_nick_doc, NULL}, + {"username", (getter)PyConnect_username_get, NULL, + PyConnect_username_doc, NULL}, + {"realname", (getter)PyConnect_realname_get, NULL, + PyConnect_realname_doc, NULL}, + {NULL} +}; + +PyTypeObject PyConnectType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Connect", /*tp_name*/ + sizeof(PyConnect), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyConnect_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyConnect objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + PyConnect_getseters, /* tp_getset */ + &PyIrssiChatBaseType, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + + +/* server connect factory function (managed == 0, don't do signal cleanup, 1 == do sig cleanup */ +PyObject *pyconnect_sub_new(void *connect, PyTypeObject *subclass, int managed) +{ + static const char *CONNECT_TYPE = "SERVER CONNECT"; + PyConnect *pyconn = NULL; + + g_return_val_if_fail(connect != NULL, NULL); + + pyconn = py_instp(PyConnect, subclass); + if (!pyconn) + return NULL; + + pyconn->base_name = CONNECT_TYPE; + pyconn->data = connect; + + if (managed) + { + //XXX: how to handle cleanup? + //signal_add_last_data("server disconnected", connect_cleanup, pyconn); + //pyconn->cleanup_installed = 1; + } + + return (PyObject *)pyconn; +} + +PyObject *pyconnect_new(void *connect, int managed) +{ + return pyconnect_sub_new(connect, &PyConnectType, managed); +} + +int connect_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyConnectType) < 0) + return 0; + + Py_INCREF(&PyConnectType); + PyModule_AddObject(py_module, "Connect", (PyObject *)&PyConnectType); + + return 1; +} diff --git a/src/objects/connect-object.h b/src/objects/connect-object.h new file mode 100644 index 0000000..53bd509 --- /dev/null +++ b/src/objects/connect-object.h @@ -0,0 +1,22 @@ +#ifndef _CONNECT_OBJECT_H_ +#define _CONNECT_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +/* forward */ +struct _SERVER_CONNECT_REC; + +typedef struct +{ + PyIrssi_HEAD(struct _SERVER_CONNECT_REC) +} PyConnect; + +extern PyTypeObject PyConnectType; + +int connect_object_init(void); +PyObject *pyconnect_sub_new(void *connect, PyTypeObject *subtype, int managed); +PyObject *pyconnect_new(void *connect, int managed); +#define pyconnect_check(op) PyObject_TypeCheck(op, &PyConnectType) + +#endif diff --git a/src/objects/dcc-chat-object.c b/src/objects/dcc-chat-object.c new file mode 100644 index 0000000..5cfe796 --- /dev/null +++ b/src/objects/dcc-chat-object.c @@ -0,0 +1,137 @@ +#include <Python.h> +#include "pyirssi_irc.h" +#include "pymodule.h" +#include "dcc-chat-object.h" +#include "factory.h" +#include "pycore.h" + +/* inherit destroy and cleanup from DccChat type */ + +/* Getters */ +PyDoc_STRVAR(PyDccChat_id_doc, + "Unique identifier - usually same as nick" +); +static PyObject *PyDccChat_id_get(PyDccChat *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->id); +} + +PyDoc_STRVAR(PyDccChat_mirc_ctcp_doc, + "Send CTCPs without the CTCP_MESSAGE prefix" +); +static PyObject *PyDccChat_mirc_ctcp_get(PyDccChat *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->mirc_ctcp); +} + +PyDoc_STRVAR(PyDccChat_connection_lost_doc, + "Other side closed connection" +); +static PyObject *PyDccChat_connection_lost_get(PyDccChat *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->connection_lost); +} + +/* specialized getters/setters */ +static PyGetSetDef PyDccChat_getseters[] = { + {"id", (getter)PyDccChat_id_get, NULL, + PyDccChat_id_doc, NULL}, + {"mirc_ctcp", (getter)PyDccChat_mirc_ctcp_get, NULL, + PyDccChat_mirc_ctcp_doc, NULL}, + {"connection_lost", (getter)PyDccChat_connection_lost_get, NULL, + PyDccChat_connection_lost_doc, NULL}, + {NULL} +}; + +/* Methods */ +PyDoc_STRVAR(PyDccChat_chat_send_doc, + "chat_send(data) -> None\n" + "\n" + "Send data to a dcc chat session.\n" +); +static PyObject *PyDccChat_chat_send(PyDccChat *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"data", NULL}; + char *data = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &data)) + return NULL; + + dcc_chat_send(self->data, data); + + Py_RETURN_NONE; +} + +/* Methods for object */ +static PyMethodDef PyDccChat_methods[] = { + {"chat_send", (PyCFunction)PyDccChat_chat_send, METH_VARARGS | METH_KEYWORDS, + PyDccChat_chat_send_doc}, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyDccChatType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "DccChat", /*tp_name*/ + sizeof(PyDccChat), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyDccChat objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyDccChat_methods, /* tp_methods */ + 0, /* tp_members */ + PyDccChat_getseters, /* tp_getset */ + &PyDccType, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + +PyObject *pydcc_chat_new(void *dcc) +{ + static const char *name = "DCC CHAT"; + return pydcc_sub_new(dcc, name, &PyDccChatType); +} + +int dcc_chat_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyDccChatType) < 0) + return 0; + + Py_INCREF(&PyDccChatType); + PyModule_AddObject(py_module, "DccChat", (PyObject *)&PyDccChatType); + + return 1; +} diff --git a/src/objects/dcc-chat-object.h b/src/objects/dcc-chat-object.h new file mode 100644 index 0000000..955e6be --- /dev/null +++ b/src/objects/dcc-chat-object.h @@ -0,0 +1,21 @@ +#ifndef _DCC_CHAT_OBJECT_H_ +#define _DCC_CHAT_OBJECT_H_ + +#include <Python.h> +#include "dcc-object.h" + +/* forward */ +struct CHAT_DCC_REC; + +typedef struct +{ + PyDcc_HEAD(struct CHAT_DCC_REC) +} PyDccChat; + +extern PyTypeObject PyDccChatType; + +PyObject *pydcc_chat_new(void *dcc); +#define pydcc_chat_check(op) PyObject_TypeCheck(op, &PyDccChatType) +int dcc_chat_object_init(void); + +#endif diff --git a/src/objects/dcc-get-object.c b/src/objects/dcc-get-object.c new file mode 100644 index 0000000..fa7c13a --- /dev/null +++ b/src/objects/dcc-get-object.c @@ -0,0 +1,138 @@ +#include <Python.h> +#include "pyirssi_irc.h" +#include "pymodule.h" +#include "dcc-get-object.h" +#include "factory.h" +#include "pycore.h" + +#define DCC_GET_CAST(rec) ((GET_DCC_REC *)rec) + +/* inherit destroy and cleanup from Dcc type */ + +/* Getters */ +PyDoc_STRVAR(PyDccGet_size_doc, + "File size" +); +static PyObject *PyDccGet_size_get(PyDccGet *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromUnsignedLong(DCC_GET_CAST(self->data)->size); +} + +PyDoc_STRVAR(PyDccGet_skipped_doc, + "Bytes skipped from start (resuming file)" +); +static PyObject *PyDccGet_skipped_get(PyDccGet *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromUnsignedLong(DCC_GET_CAST(self->data)->skipped); +} + +PyDoc_STRVAR(PyDccGet_get_type_doc, + "What to do if file exists? 0=default, 1=rename, 2=overwrite, 3=resume" +); +static PyObject *PyDccGet_get_type_get(PyDccGet *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(DCC_GET_CAST(self->data)->get_type); +} + +PyDoc_STRVAR(PyDccGet_file_doc, + "The real file name which we use." +); +static PyObject *PyDccGet_file_get(PyDccGet *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(DCC_GET_CAST(self->data)->file); +} + +PyDoc_STRVAR(PyDccGet_file_quoted_doc, + "true if file name was received quoted (\"file name\")" +); +static PyObject *PyDccGet_file_quoted_get(PyDccGet *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(DCC_GET_CAST(self->data)->file_quoted); +} + +/* specialized getters/setters */ +static PyGetSetDef PyDccGet_getseters[] = { + {"size", (getter)PyDccGet_size_get, NULL, + PyDccGet_size_doc, NULL}, + {"skipped", (getter)PyDccGet_skipped_get, NULL, + PyDccGet_skipped_doc, NULL}, + {"get_type", (getter)PyDccGet_get_type_get, NULL, + PyDccGet_get_type_doc, NULL}, + {"file", (getter)PyDccGet_file_get, NULL, + PyDccGet_file_doc, NULL}, + {"file_quoted", (getter)PyDccGet_file_quoted_get, NULL, + PyDccGet_file_quoted_doc, NULL}, + {NULL} +}; + +/* Methods */ +/* Methods for object */ +static PyMethodDef PyDccGet_methods[] = { + {NULL} /* Sentinel */ +}; + +PyTypeObject PyDccGetType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "DccGet", /*tp_name*/ + sizeof(PyDccGet), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyDccGet objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyDccGet_methods, /* tp_methods */ + 0, /* tp_members */ + PyDccGet_getseters, /* tp_getset */ + &PyDccType, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + +PyObject *pydcc_get_new(void *dcc) +{ + static const char *name = "DCC GET"; + return pydcc_sub_new(dcc, name, &PyDccGetType); +} + +int dcc_get_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyDccGetType) < 0) + return 0; + + Py_INCREF(&PyDccGetType); + PyModule_AddObject(py_module, "DccGet", (PyObject *)&PyDccGetType); + + return 1; +} diff --git a/src/objects/dcc-get-object.h b/src/objects/dcc-get-object.h new file mode 100644 index 0000000..532b854 --- /dev/null +++ b/src/objects/dcc-get-object.h @@ -0,0 +1,18 @@ +#ifndef _DCC_GET_OBJECT_H_ +#define _DCC_GET_OBJECT_H_ + +#include <Python.h> +#include "dcc-object.h" + +typedef struct +{ + PyDcc_HEAD(void) +} PyDccGet; + +extern PyTypeObject PyDccGetType; + +PyObject *pydcc_get_new(void *dcc); +#define pydcc_get_check(op) PyObject_TypeCheck(op, &PyDccGetType) +int dcc_get_object_init(void); + +#endif diff --git a/src/objects/dcc-object.c b/src/objects/dcc-object.c new file mode 100644 index 0000000..95a3a4a --- /dev/null +++ b/src/objects/dcc-object.c @@ -0,0 +1,347 @@ +#include <Python.h> +#include "pyirssi_irc.h" +#include "pymodule.h" +#include "dcc-object.h" +#include "factory.h" +#include "pycore.h" + + +/* monitor "dcc destroyed signal" */ +static void dcc_cleanup(DCC_REC *dcc) +{ + PyDcc *pydcc = signal_get_user_data(); + + if (dcc == pydcc->data) + { + pydcc->data = NULL; + pydcc->cleanup_installed = 0; + signal_remove_data("dcc destroyed", dcc_cleanup, pydcc); + } +} + +static void PyDcc_dealloc(PyDcc *self) +{ + if (self->cleanup_installed) + signal_remove_data("dcc destroyed", dcc_cleanup, self); + + Py_XDECREF(self->server); + Py_XDECREF(self->chat); + + self->ob_type->tp_free((PyObject*)self); +} + +/* Getters */ +PyDoc_STRVAR(PyDcc_orig_type_doc, + "Original DCC type that was sent to us - same as type except GET and SEND are swapped" +); +static PyObject *PyDcc_orig_type_get(PyDcc *self, void *closure) +{ + const char *type; + + RET_NULL_IF_INVALID(self->data); + + type = module_find_id_str("DCC", DCC(self->data)->orig_type); + RET_AS_STRING_OR_NONE(type); +} + +PyDoc_STRVAR(PyDcc_created_doc, + "Time stamp when the DCC record was created" +); +static PyObject *PyDcc_created_get(PyDcc *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromUnsignedLong(DCC(self->data)->created); +} + +PyDoc_STRVAR(PyDcc_server_doc, + "Server record where the DCC was initiated." +); +static PyObject *PyDcc_server_get(PyDcc *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_OBJ_OR_NONE(self->server); +} + +PyDoc_STRVAR(PyDcc_servertag_doc, + "Tag of the server where the DCC was initiated." +); +static PyObject *PyDcc_servertag_get(PyDcc *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(DCC(self->data)->servertag); +} + +PyDoc_STRVAR(PyDcc_mynick_doc, + "Our nick to use in DCC chat." +); +static PyObject *PyDcc_mynick_get(PyDcc *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(DCC(self->data)->mynick); +} + +PyDoc_STRVAR(PyDcc_nick_doc, + "Other side's nick name." +); +static PyObject *PyDcc_nick_get(PyDcc *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(DCC(self->data)->nick); +} + +PyDoc_STRVAR(PyDcc_chat_doc, + "Dcc chat record if the request came through DCC chat" +); +static PyObject *PyDcc_chat_get(PyDcc *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_OBJ_OR_NONE(self->chat); +} + +PyDoc_STRVAR(PyDcc_target_doc, + "Who the request was sent to - your nick, channel or empty if you sent the request" +); +static PyObject *PyDcc_target_get(PyDcc *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(DCC(self->data)->target); +} + +PyDoc_STRVAR(PyDcc_arg_doc, + "Given argument .. file name usually" +); +static PyObject *PyDcc_arg_get(PyDcc *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(DCC(self->data)->arg); +} + +PyDoc_STRVAR(PyDcc_addr_doc, + "Other side's IP address." +); +static PyObject *PyDcc_addr_get(PyDcc *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(DCC(self->data)->addrstr); +} + +PyDoc_STRVAR(PyDcc_port_doc, + "Port we're connecting in." +); +static PyObject *PyDcc_port_get(PyDcc *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(DCC(self->data)->port); +} + +PyDoc_STRVAR(PyDcc_starttime_doc, + "Unix time stamp when the DCC transfer was started" +); +static PyObject *PyDcc_starttime_get(PyDcc *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromUnsignedLong(DCC(self->data)->starttime); +} + +PyDoc_STRVAR(PyDcc_transfd_doc, + "Bytes transferred" +); +static PyObject *PyDcc_transfd_get(PyDcc *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromUnsignedLong(DCC(self->data)->transfd); +} + +/* specialized getters/setters */ +static PyGetSetDef PyDcc_getseters[] = { + {"orig_type", (getter)PyDcc_orig_type_get, NULL, + PyDcc_orig_type_doc, NULL}, + {"created", (getter)PyDcc_created_get, NULL, + PyDcc_created_doc, NULL}, + {"server", (getter)PyDcc_server_get, NULL, + PyDcc_server_doc, NULL}, + {"servertag", (getter)PyDcc_servertag_get, NULL, + PyDcc_servertag_doc, NULL}, + {"mynick", (getter)PyDcc_mynick_get, NULL, + PyDcc_mynick_doc, NULL}, + {"nick", (getter)PyDcc_nick_get, NULL, + PyDcc_nick_doc, NULL}, + {"chat", (getter)PyDcc_chat_get, NULL, + PyDcc_chat_doc, NULL}, + {"target", (getter)PyDcc_target_get, NULL, + PyDcc_target_doc, NULL}, + {"arg", (getter)PyDcc_arg_get, NULL, + PyDcc_arg_doc, NULL}, + {"addr", (getter)PyDcc_addr_get, NULL, + PyDcc_addr_doc, NULL}, + {"port", (getter)PyDcc_port_get, NULL, + PyDcc_port_doc, NULL}, + {"starttime", (getter)PyDcc_starttime_get, NULL, + PyDcc_starttime_doc, NULL}, + {"transfd", (getter)PyDcc_transfd_get, NULL, + PyDcc_transfd_doc, NULL}, + {NULL} +}; + +/* Methods */ +PyDoc_STRVAR(PyDcc_destroy_doc, + "destroy() -> None\n" + "\n" + "Destroy DCC connection\n" +); +static PyObject *PyDcc_destroy(PyDcc *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + dcc_destroy(DCC(self->data)); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyDcc_reject_doc, + "reject() -> None\n" + "\n" + "?\n" +); +static PyObject *PyDcc_reject(PyDcc *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"server", NULL}; + PyObject *server = NULL; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, + &server)) + return NULL; + + if (!pyirc_server_check(server)) + return PyErr_Format(PyExc_TypeError, "arg must be IRC server object"); + + dcc_reject(self->data, ((PyIrcServer*)server)->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyDcc_close_doc, + "close() -> None\n" + "\n" + "Close and destroy DCC connection.\n" +); +static PyObject *PyDcc_close(PyDcc *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + dcc_close(self->data); + + Py_RETURN_NONE; +} + +/* Methods for object */ +static PyMethodDef PyDcc_methods[] = { + {"destroy", (PyCFunction)PyDcc_destroy, METH_NOARGS, + PyDcc_destroy_doc}, + {"reject", (PyCFunction)PyDcc_reject, METH_VARARGS | METH_KEYWORDS, + PyDcc_reject_doc}, + {"close", (PyCFunction)PyDcc_close, METH_NOARGS, + PyDcc_close_doc}, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyDccType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Dcc", /*tp_name*/ + sizeof(PyDcc), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyDcc_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyDcc objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyDcc_methods, /* tp_methods */ + 0, /* tp_members */ + PyDcc_getseters, /* tp_getset */ + &PyIrssiBaseType, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + + +/* Dcc factory function */ +PyObject *pydcc_sub_new(void *dcc, const char *name, PyTypeObject *subclass) +{ + PyObject *chat = NULL, *server = NULL; + PyDcc *pydcc; + DCC_REC *rec = dcc; + + server = py_irssi_chat_new(rec->server, 1); + if (!server) + return NULL; + + chat = py_irssi_chat_new(rec->chat, 1); + if (!chat) + { + Py_DECREF(server); + return NULL; + } + + pydcc = py_instp(PyDcc, subclass); + if (!pydcc) + { + Py_DECREF(server); + Py_DECREF(chat); + return NULL; + } + + pydcc->data = dcc; + pydcc->server = server; + pydcc->chat = chat; + pydcc->base_name = name; + + pydcc->cleanup_installed = 1; + signal_add_last_data("dcc destroyed", dcc_cleanup, pydcc); + + return (PyObject *)pydcc; +} + +PyObject *pydcc_new(void *dcc) +{ + static const char *name = "DCC"; + return pydcc_sub_new(dcc, name, &PyDccType); +} + +int dcc_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyDccType) < 0) + return 0; + + Py_INCREF(&PyDccType); + PyModule_AddObject(py_module, "Dcc", (PyObject *)&PyDccType); + + return 1; +} diff --git a/src/objects/dcc-object.h b/src/objects/dcc-object.h new file mode 100644 index 0000000..d44d3db --- /dev/null +++ b/src/objects/dcc-object.h @@ -0,0 +1,24 @@ +#ifndef _DCC_OBJECT_H_ +#define _DCC_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +#define PyDcc_HEAD(type) \ + PyIrssi_HEAD(type) \ + PyObject *server; \ + PyObject *chat; + +typedef struct +{ + PyDcc_HEAD(void) +} PyDcc; + +extern PyTypeObject PyDccType; + +PyObject *pydcc_sub_new(void *dcc, const char *name, PyTypeObject *subclass); +PyObject *pydcc_new(void *dcc); +#define pydcc_check(op) PyObject_TypeCheck(op, &PyDccType) +int dcc_object_init(void); + +#endif diff --git a/src/objects/dcc-send-object.c b/src/objects/dcc-send-object.c new file mode 100644 index 0000000..643f741 --- /dev/null +++ b/src/objects/dcc-send-object.c @@ -0,0 +1,138 @@ +#include <Python.h> +#include "pyirssi_irc.h" +#include "pymodule.h" +#include "dcc-send-object.h" +#include "factory.h" +#include "pycore.h" + +#define DCC_SEND_CAST(rec) ((SEND_DCC_REC *)rec) + +/* inherit destroy and cleanup from Dcc type */ + +/* Getters */ +PyDoc_STRVAR(PyDccSend_size_doc, + "File size" +); +static PyObject *PyDccSend_size_get(PyDccSend *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromUnsignedLong(DCC_SEND_CAST(self->data)->size); +} + +PyDoc_STRVAR(PyDccSend_skipped_doc, + "Bytes skipped from start (resuming file)" +); +static PyObject *PyDccSend_skipped_get(PyDccSend *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromUnsignedLong(DCC_SEND_CAST(self->data)->skipped); +} + +PyDoc_STRVAR(PyDccSend_file_quoted_doc, + "True if file name was received quoted (\"file name\")" +); +static PyObject *PyDccSend_file_quoted_get(PyDccSend *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(DCC_SEND_CAST(self->data)->file_quoted); +} + +PyDoc_STRVAR(PyDccSend_waitforend_doc, + "File is sent, just wait for the replies from the other side" +); +static PyObject *PyDccSend_waitforend_get(PyDccSend *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(DCC_SEND_CAST(self->data)->waitforend); +} + +PyDoc_STRVAR(PyDccSend_gotalldata_doc, + "Got all acks from the other end" +); +static PyObject *PyDccSend_gotalldata_get(PyDccSend *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(DCC_SEND_CAST(self->data)->gotalldata); +} + +/* specialized getters/setters */ +static PyGetSetDef PyDccSend_getseters[] = { + {"size", (getter)PyDccSend_size_get, NULL, + PyDccSend_size_doc, NULL}, + {"skipped", (getter)PyDccSend_skipped_get, NULL, + PyDccSend_skipped_doc, NULL}, + {"file_quoted", (getter)PyDccSend_file_quoted_get, NULL, + PyDccSend_file_quoted_doc, NULL}, + {"waitforend", (getter)PyDccSend_waitforend_get, NULL, + PyDccSend_waitforend_doc, NULL}, + {"gotalldata", (getter)PyDccSend_gotalldata_get, NULL, + PyDccSend_gotalldata_doc, NULL}, + {NULL} +}; + +/* Methods */ +/* Methods for object */ +static PyMethodDef PyDccSend_methods[] = { + {NULL} /* Sentinel */ +}; + +PyTypeObject PyDccSendType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "DccSend", /*tp_name*/ + sizeof(PyDccSend), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyDccSend objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyDccSend_methods, /* tp_methods */ + 0, /* tp_members */ + PyDccSend_getseters, /* tp_getset */ + &PyDccType, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + +PyObject *pydcc_send_new(void *dcc) +{ + static const char *name = "DCC SEND"; + return pydcc_sub_new(dcc, name, &PyDccSendType); +} + +int dcc_send_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyDccSendType) < 0) + return 0; + + Py_INCREF(&PyDccSendType); + PyModule_AddObject(py_module, "DccSend", (PyObject *)&PyDccSendType); + + return 1; +} diff --git a/src/objects/dcc-send-object.h b/src/objects/dcc-send-object.h new file mode 100644 index 0000000..6303f86 --- /dev/null +++ b/src/objects/dcc-send-object.h @@ -0,0 +1,18 @@ +#ifndef _DCC_SEND_OBJECT_H_ +#define _DCC_SEND_OBJECT_H_ + +#include <Python.h> +#include "dcc-object.h" + +typedef struct +{ + PyDcc_HEAD(void) +} PyDccSend; + +extern PyTypeObject PyDccSendType; + +PyObject *pydcc_send_new(void *dcc); +#define pydcc_send_check(op) PyObject_TypeCheck(op, &PyDccSendType) +int dcc_send_object_init(void); + +#endif diff --git a/src/objects/factory.c b/src/objects/factory.c new file mode 100644 index 0000000..49336e6 --- /dev/null +++ b/src/objects/factory.c @@ -0,0 +1,326 @@ +#include <Python.h> +#include "pyirssi.h" +#include "factory.h" + +/* Irssi object factory works for all items with at least a type member. + * + * Use py_irssi_new() or py_irssi_chat_new() to get a new wrapper for an + * IrssiObject or an IrssiChatObject, respectively. + * + * For objects not descending from IrssiObject or IrssiChatObject, you must + * use the object-specific init function directly. + */ + +#define MAKEKEY(type, chat) ((chat << 16 ) | type) +#define GETTYPE(key) (key & 0xffff) +#define GETCHAT(key) ((key >> 16) & 0xffff) + + +GHashTable *init_map = NULL; + +static int init_objects(void); +static void register_chat(CHAT_PROTOCOL_REC *rec); +static void unregister_chat(CHAT_PROTOCOL_REC *rec); +static void insert_map(int type, int chat_type, InitFunc func); +static int remove_chat(void *key, void *value, void *chat_typep); +static void register_nonchat(void); +static InitFunc find_map(int type, int chat_type); + +static int init_objects(void) +{ + if (!pyscript_init()) + return 0; + + /* order is somewhat important here */ + if (!base_objects_init()) + return 0; + + if (!window_item_object_init()) + return 0; + + if (!channel_object_init()) + return 0; + + if (!query_object_init()) + return 0; + + if (!server_object_init()) + return 0; + + if (!connect_object_init()) + return 0; + + if (!irc_server_object_init()) + return 0; + + if (!irc_connect_object_init()) + return 0; + + if (!irc_channel_object_init()) + return 0; + + if (!ban_object_init()) + return 0; + + if (!nick_object_init()) + return 0; + + if (!chatnet_object_init()) + return 0; + + if (!reconnect_object_init()) + return 0; + + if (!window_object_init()) + return 0; + + if (!textdest_object_init()) + return 0; + + if (!rawlog_object_init()) + return 0; + + if (!log_object_init()) + return 0; + + if (!logitem_object_init()) + return 0; + + if (!ignore_object_init()) + return 0; + + if (!dcc_object_init()) + return 0; + + if (!dcc_chat_object_init()) + return 0; + + if (!dcc_get_object_init()) + return 0; + + if (!dcc_send_object_init()) + return 0; + + if (!netsplit_object_init()) + return 0; + + if (!netsplit_server_object_init()) + return 0; + + if (!netsplit_channel_object_init()) + return 0; + + if (!notifylist_object_init()) + return 0; + + if (!process_object_init()) + return 0; + + if (!command_object_init()) + return 0; + + if (!theme_object_init()) + return 0; + + if (!statusbar_item_object_init()) + return 0; + + if (!main_window_object_init()) + return 0; + + return 1; +} + +static InitFunc find_map(int type, int chat_type) +{ + unsigned hash; + + g_return_val_if_fail(type <= 0xffff, NULL); + g_return_val_if_fail(chat_type <= 0xffff, NULL); + + hash = MAKEKEY(type, chat_type); + return g_hash_table_lookup(init_map, GUINT_TO_POINTER(hash)); +} + +static void insert_map(int type, int chat_type, InitFunc func) +{ + unsigned hash; + + g_return_if_fail(type <= 0xffff); + g_return_if_fail(chat_type <= 0xffff); + + hash = MAKEKEY(type, chat_type); + g_hash_table_insert(init_map, GUINT_TO_POINTER(hash), func); +} + +static void register_chat(CHAT_PROTOCOL_REC *rec) +{ + int type; + int chat_type; + int is_irc = 0; + + /* chat_type == rec->id ??? */ + chat_type = chat_protocol_lookup(rec->name); + g_return_if_fail(chat_type >= 0 && chat_type < 0xffff); + + if (!g_strcasecmp(rec->name, "IRC")) + is_irc = 1; + + type = module_get_uniq_id("SERVER", 0); + if (is_irc) + insert_map(type, chat_type, (InitFunc)pyirc_server_new); + else + insert_map(type, chat_type, (InitFunc)pyserver_new); + + type = module_get_uniq_id("SERVER CONNECT", 0); + if (is_irc) + insert_map(type, chat_type, (InitFunc)pyirc_connect_new); + else + insert_map(type, chat_type, (InitFunc)pyconnect_new); + + type = module_get_uniq_id_str("WINDOW ITEM TYPE", "CHANNEL"); + if (is_irc) + insert_map(type, chat_type, (InitFunc)pyirc_channel_new); + else + insert_map(type, chat_type, (InitFunc)pychannel_new); + + type = module_get_uniq_id_str("WINDOW ITEM TYPE", "QUERY"); + insert_map(type, chat_type, (InitFunc)pyquery_new); + + type = module_get_uniq_id("CHATNET", 0); + insert_map(type, chat_type, (InitFunc)pychatnet_new); + + type = module_get_uniq_id("NICK", 0); + insert_map(type, chat_type, (InitFunc)pynick_new); +} + +/* register funcs for objects without a chat type */ +static void register_nonchat(void) +{ + int type; + int chat_type = 0xffff; + + type = module_get_uniq_id_str("DCC", "CHAT"); + insert_map(type, chat_type, (InitFunc)pydcc_chat_new); + + type = module_get_uniq_id_str("DCC", "GET"); + insert_map(type, chat_type, (InitFunc)pydcc_get_new); + + type = module_get_uniq_id_str("DCC", "SEND"); + insert_map(type, chat_type, (InitFunc)pydcc_send_new); + + type = module_get_uniq_id_str("DCC", "SERVER"); + insert_map(type, chat_type, (InitFunc)pydcc_new); +} + +static int remove_chat(void *key, void *value, void *chat_typep) +{ + unsigned hash = GPOINTER_TO_UINT(key); + int chat_type = GPOINTER_TO_INT(chat_type); + + if (GETCHAT(hash) == chat_type) + return TRUE; + + return FALSE; +} + +/* remove all items matching chat_type */ +static void unregister_chat(CHAT_PROTOCOL_REC *rec) +{ + /*int chat_type = chat_protocol_lookup(rec->name);*/ + g_hash_table_foreach_remove(init_map, + (GHRFunc)remove_chat, + GINT_TO_POINTER(rec->id)); +} + +PyObject *py_irssi_new(void *typeobj, int managed) +{ + IRSSI_BASE_REC *base = typeobj; + InitFunc ifunc; + + if (!base) + Py_RETURN_NONE; + + ifunc = find_map(base->type, 0xffff); + + if (ifunc) + return ifunc(typeobj, managed); + + return PyErr_Format(PyExc_RuntimeError, "no initfunc for object type %d", base->type); +} + +PyObject *py_irssi_chat_new(void *typeobj, int managed) +{ + IRSSI_CHAT_REC *chat = typeobj; + InitFunc ifunc; + + if (!chat) + Py_RETURN_NONE; + + ifunc = find_map(chat->type, chat->chat_type); + + if (ifunc) + return ifunc(typeobj, managed); + + return PyErr_Format(PyExc_RuntimeError, "no initfunc for object type %d, chat_type %d", + chat->type, chat->chat_type); +} + +PyObject *py_irssi_objlist_new(GSList *node, int managed, InitFunc init) +{ + PyObject *list = NULL; + + list = PyList_New(0); + if (!list) + goto error; + + for (; node != NULL; node = node->next) + { + int ret; + PyObject *obj = init(node->data, managed); + + if (!obj) + goto error; + + ret = PyList_Append(list, obj); + Py_DECREF(obj); + + if (ret != 0) + goto error; + } + + return list; + +error: + Py_XDECREF(list); + return NULL; +} + +int factory_init(void) +{ + g_return_val_if_fail(init_map == NULL, 0); + + if (!init_objects()) + return 0; + + init_map = g_hash_table_new(g_direct_hash, g_direct_equal); + g_slist_foreach(chat_protocols, (GFunc) register_chat, NULL); + register_nonchat(); + + signal_add("chat protocol created", (SIGNAL_FUNC) register_chat); + signal_add("chat protocol destroyed", (SIGNAL_FUNC) unregister_chat); + + return 1; +} + +void factory_deinit(void) +{ + g_return_if_fail(init_map != NULL); + + g_hash_table_destroy(init_map); + init_map = NULL; + + signal_remove("chat protocol created", (SIGNAL_FUNC) register_chat); + signal_remove("chat protocol destroyed", (SIGNAL_FUNC) unregister_chat); +} + diff --git a/src/objects/factory.h b/src/objects/factory.h new file mode 100644 index 0000000..9d4bd79 --- /dev/null +++ b/src/objects/factory.h @@ -0,0 +1,58 @@ +#ifndef _FACTORY_H_ +#define _FACTORY_H_ + +#include <Python.h> +#include "pyscript-object.h" +#include "base-objects.h" +#include "window-item-object.h" +#include "channel-object.h" +#include "query-object.h" +#include "server-object.h" +#include "connect-object.h" +#include "irc-server-object.h" +#include "irc-connect-object.h" +#include "irc-channel-object.h" +#include "ban-object.h" +#include "nick-object.h" +#include "chatnet-object.h" +#include "reconnect-object.h" +#include "window-object.h" +#include "textdest-object.h" +#include "rawlog-object.h" +#include "log-object.h" +#include "logitem-object.h" +#include "ignore-object.h" +#include "dcc-object.h" +#include "dcc-chat-object.h" +#include "dcc-get-object.h" +#include "dcc-send-object.h" +#include "netsplit-object.h" +#include "netsplit-server-object.h" +#include "netsplit-channel-object.h" +#include "notifylist-object.h" +#include "process-object.h" +#include "command-object.h" +#include "theme-object.h" +#include "statusbar-item-object.h" +#include "main-window-object.h" + +int factory_init(void); +void factory_deinit(void); + +/*managed == 1: object invalidates itself + *managed == 0: caller responsible for invalidating object + *XXX: most objects invalidate themselves, ignoring "managed" switch, + * and some are never managed (Reconnect) + */ + +/* For objects with a type member but no chat_type */ +PyObject *py_irssi_new(void *typeobj, int managed); +/* For objects with both type and chat_type members */ +PyObject *py_irssi_chat_new(void *typeobj, int managed); + +typedef PyObject *(*InitFunc)(void *, int); +PyObject *py_irssi_objlist_new(GSList *node, int managed, InitFunc init); +#define py_irssi_chatlist_new(n, m) py_irssi_objlist_new(n, m, py_irssi_chat_new) +#define py_irssi_list_new(n, m) py_irssi_objlist_new(n, m, py_irssi_new) + +#endif diff --git a/src/objects/ignore-object.c b/src/objects/ignore-object.c new file mode 100644 index 0000000..979c9b1 --- /dev/null +++ b/src/objects/ignore-object.c @@ -0,0 +1,284 @@ +#include <Python.h> +#include "pyirssi_irc.h" +#include "pymodule.h" +#include "ignore-object.h" +#include "factory.h" +#include "pycore.h" + +/* monitor "ignore destroy" signal */ +static void ignore_cleanup(IGNORE_REC *ignore) +{ + PyIgnore *pyignore = signal_get_user_data(); + + if (ignore == pyignore->data) + { + pyignore->data = NULL; + pyignore->cleanup_installed = 0; + signal_remove_data("ignore destroy", ignore_cleanup, pyignore); + } +} + +static void PyIgnore_dealloc(PyIgnore *self) +{ + if (self->cleanup_installed) + signal_remove_data("ignore destroy", ignore_cleanup, self); + + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyIgnore_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyIgnore *self; + + self = (PyIgnore *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* Getters */ +PyDoc_STRVAR(PyIgnore_mask_doc, + "Ignore mask" +); +static PyObject *PyIgnore_mask_get(PyIgnore *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->mask); +} + +PyDoc_STRVAR(PyIgnore_servertag_doc, + "Ignore only in server" +); +static PyObject *PyIgnore_servertag_get(PyIgnore *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->servertag); +} + +PyDoc_STRVAR(PyIgnore_pattern_doc, + "Ignore text patern" +); +static PyObject *PyIgnore_pattern_get(PyIgnore *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->pattern); +} + +PyDoc_STRVAR(PyIgnore_level_doc, + "Ignore level" +); +static PyObject *PyIgnore_level_get(PyIgnore *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->level); +} + +PyDoc_STRVAR(PyIgnore_exception_doc, + "This is an exception ignore" +); +static PyObject *PyIgnore_exception_get(PyIgnore *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->exception); +} + +PyDoc_STRVAR(PyIgnore_regexp_doc, + "Regexp pattern matching" +); +static PyObject *PyIgnore_regexp_get(PyIgnore *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->regexp); +} + +PyDoc_STRVAR(PyIgnore_fullword_doc, + "Pattern matches only full words" +); +static PyObject *PyIgnore_fullword_get(PyIgnore *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->fullword); +} + +PyDoc_STRVAR(PyIgnore_replies_doc, + "Ignore replies to nick in channel" +); +static PyObject *PyIgnore_replies_get(PyIgnore *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->replies); +} + +/* specialized getters/setters */ +static PyGetSetDef PyIgnore_getseters[] = { + {"mask", (getter)PyIgnore_mask_get, NULL, + PyIgnore_mask_doc, NULL}, + {"servertag", (getter)PyIgnore_servertag_get, NULL, + PyIgnore_servertag_doc, NULL}, + {"pattern", (getter)PyIgnore_pattern_get, NULL, + PyIgnore_pattern_doc, NULL}, + {"level", (getter)PyIgnore_level_get, NULL, + PyIgnore_level_doc, NULL}, + {"exception", (getter)PyIgnore_exception_get, NULL, + PyIgnore_exception_doc, NULL}, + {"regexp", (getter)PyIgnore_regexp_get, NULL, + PyIgnore_regexp_doc, NULL}, + {"fullword", (getter)PyIgnore_fullword_get, NULL, + PyIgnore_fullword_doc, NULL}, + {"replies", (getter)PyIgnore_replies_get, NULL, + PyIgnore_replies_doc, NULL}, + {NULL} +}; + +/* Methods */ +PyDoc_STRVAR(PyIgnore_channels_doc, + "channels() -> list of str\n" + "\n" + "Ignore only in channels (list of names)\n" +); +static PyObject *PyIgnore_channels(PyIgnore *self, PyObject *args) +{ + char **p; + PyObject *list; + + RET_NULL_IF_INVALID(self->data); + + list = PyList_New(0); + if (!list) + return NULL; + + for (p = self->data->channels; *p; p++) + { + int ret; + PyObject *str; + + str = PyString_FromString(*p); + if (!str) + { + Py_XDECREF(list); + return NULL; + } + + ret = PyList_Append(list, str); + Py_DECREF(str); + if (ret != 0) + { + Py_XDECREF(list); + return NULL; + } + } + + return list; +} + +PyDoc_STRVAR(PyIgnore_add_rec_doc, + "add_rec() -> None\n" + "\n" + "Add ignore record" +); +static PyObject *PyIgnore_add_rec(PyIgnore *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + ignore_add_rec(self->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyIgnore_update_rec_doc, + "update_rec() -> None\n" + "\n" + "Update ignore record in configuration" +); +static PyObject *PyIgnore_update_rec(PyIgnore *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + ignore_update_rec(self->data); + + Py_RETURN_NONE; +} + +/* Methods for object */ +static PyMethodDef PyIgnore_methods[] = { + {"add_rec", (PyCFunction)PyIgnore_add_rec, METH_NOARGS, + PyIgnore_add_rec_doc}, + {"update_rec", (PyCFunction)PyIgnore_update_rec, METH_NOARGS, + PyIgnore_update_rec_doc}, + {"channels", (PyCFunction)PyIgnore_channels, METH_NOARGS, + PyIgnore_channels_doc}, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyIgnoreType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Ignore", /*tp_name*/ + sizeof(PyIgnore), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyIgnore_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyIgnore objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyIgnore_methods, /* tp_methods */ + 0, /* tp_members */ + PyIgnore_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyIgnore_new, /* tp_new */ +}; + + +/* ignore factory function */ +PyObject *pyignore_new(void *ignore) +{ + PyIgnore *pyignore; + + pyignore = py_inst(PyIgnore, PyIgnoreType); + if (!pyignore) + return NULL; + + pyignore->data = ignore; + pyignore->cleanup_installed = 1; + signal_add_last_data("ignore destroy", ignore_cleanup, pyignore); + + return (PyObject *)pyignore; +} + +int ignore_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyIgnoreType) < 0) + return 0; + + Py_INCREF(&PyIgnoreType); + PyModule_AddObject(py_module, "Ignore", (PyObject *)&PyIgnoreType); + + return 1; +} diff --git a/src/objects/ignore-object.h b/src/objects/ignore-object.h new file mode 100644 index 0000000..39f7425 --- /dev/null +++ b/src/objects/ignore-object.h @@ -0,0 +1,21 @@ +#ifndef _IGNORE_OBJECT_H_ +#define _IGNORE_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +/* forward */ +struct _IGNORE_REC; + +typedef struct +{ + PyIrssiFinal_HEAD(struct _IGNORE_REC) +} PyIgnore; + +extern PyTypeObject PyIgnoreType; + +int ignore_object_init(void); +PyObject *pyignore_new(void *ignore); +#define pyignore_check(op) PyObject_TypeCheck(op, &PyIgnoreType) + +#endif diff --git a/src/objects/irc-channel-object.c b/src/objects/irc-channel-object.c new file mode 100644 index 0000000..5e9531d --- /dev/null +++ b/src/objects/irc-channel-object.c @@ -0,0 +1,173 @@ +#include <Python.h> +#include "pyirssi_irc.h" +#include "pymodule.h" +#include "pycore.h" +#include "irc-channel-object.h" +#include "factory.h" + +/* PyIrcChannel destructor is inherited from PyChannel */ + +/* specialized getters/setters */ +static PyGetSetDef PyIrcChannel_getseters[] = { + {NULL} +}; + +/* Methods */ +PyDoc_STRVAR(bans_doc, + "bans() -> list of Ban objects\n" + "\n" + "Returns a list of bans in the channel.\n" +); +static PyObject *PyIrcChannel_bans(PyIrcChannel *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + return py_irssi_objlist_new(self->data->banlist, 1, (InitFunc)pyban_new); +} + +PyDoc_STRVAR(ban_get_mask_doc, + "ban_get_mask(nick, ban_type=0) -> str\n" + "\n" + "Get ban mask for 'nick'.\n" +); +static PyObject *PyIrcChannel_ban_get_mask(PyIrcChannel *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"nick", "ban_type", NULL}; + char *nick, *str; + int ban_type = 0; + PyObject *ret; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, &nick, &ban_type)) + return NULL; + + str = ban_get_mask(self->data, nick, ban_type); + if (!str) + Py_RETURN_NONE; + + ret = PyString_FromString(str); + g_free(str); + + return ret; +} + +PyDoc_STRVAR(banlist_add_doc, + "banlist_add(ban, nick, time) -> Ban object or None\n" + "\n" + "Add a new ban to channel. Return None if duplicate." +); +static PyObject *PyIrcChannel_banlist_add(PyIrcChannel *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"ban", "nick", "time", NULL}; + char *ban, *nick; + time_t btime; + BAN_REC *newban; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ssk", kwlist, &ban, &nick, &btime)) + return NULL; + + newban = banlist_add(self->data, ban, nick, btime); + /* XXX: return none or throw error? */ + if (!newban) + Py_RETURN_NONE; + + return pyban_new(newban); +} + +PyDoc_STRVAR(banlist_remove_doc, + "banlist_remove(ban, nick) -> None\n" + "\n" + "Remove a new ban from channel.\n" +); +static PyObject *PyIrcChannel_banlist_remove(PyIrcChannel *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"ban", "nick", NULL}; + char *ban, *nick; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist, &ban, &nick)) + return NULL; + + banlist_remove(self->data, ban, nick); + Py_RETURN_NONE; +} + +/* Methods for object */ +static PyMethodDef PyIrcChannel_methods[] = { + {"bans", (PyCFunction)PyIrcChannel_bans, METH_NOARGS, + bans_doc}, + {"ban_get_mask", (PyCFunction)PyIrcChannel_ban_get_mask, METH_VARARGS | METH_KEYWORDS, + ban_get_mask_doc}, + {"banlist_add", (PyCFunction)PyIrcChannel_banlist_add, METH_VARARGS | METH_KEYWORDS, + banlist_add_doc}, + {"banlist_remove", (PyCFunction)PyIrcChannel_banlist_remove, METH_VARARGS | METH_KEYWORDS, + banlist_remove_doc}, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyIrcChannelType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "IrcChannel", /*tp_name*/ + sizeof(PyIrcChannel), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyIrcChannel objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyIrcChannel_methods, /* tp_methods */ + 0, /* tp_members */ + PyIrcChannel_getseters, /* tp_getset */ + &PyChannelType, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + + +/* irc channel factory function */ +PyObject *pyirc_channel_new(void *chan) +{ + static const char *BASE_NAME = "CHANNEL"; + return pychannel_sub_new(chan, BASE_NAME, &PyIrcChannelType); +} + +int irc_channel_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyIrcChannelType) < 0) + return 0; + + Py_INCREF(&PyIrcChannelType); + PyModule_AddObject(py_module, "IrcChannel", (PyObject *)&PyIrcChannelType); + + return 1; +} diff --git a/src/objects/irc-channel-object.h b/src/objects/irc-channel-object.h new file mode 100644 index 0000000..a1d845a --- /dev/null +++ b/src/objects/irc-channel-object.h @@ -0,0 +1,21 @@ +#ifndef _IRC_CHANNEL_OBJECT_H_ +#define _IRC_CHANNEL_OBJECT_H_ + +#include <Python.h> +#include "window-item-object.h" + +/* forward */ +struct _IRC_CHANNEL_REC; + +typedef struct +{ + PyWindowItem_HEAD(struct _IRC_CHANNEL_REC) +} PyIrcChannel; + +extern PyTypeObject PyIrcChannelType; + +int irc_channel_object_init(void); +PyObject *pyirc_channel_new(void *chan); +#define pyirc_channel_check(op) PyObject_TypeCheck(op, &PyIrcChannelType) + +#endif diff --git a/src/objects/irc-connect-object.c b/src/objects/irc-connect-object.c new file mode 100644 index 0000000..5068dea --- /dev/null +++ b/src/objects/irc-connect-object.c @@ -0,0 +1,86 @@ +#include <Python.h> +#include "pymodule.h" +#include "base-objects.h" +#include "irc-connect-object.h" +#include "pyirssi_irc.h" +#include "pycore.h" +#include "pyutils.h" + +/* cleanup and deallocation handled by Connect base */ + +/* Getters */ +PyDoc_STRVAR(PyIrcConnect_alternate_nick_doc, + "Alternate nick to use if default nick is taken" +); +static PyObject *PyIrcConnect_alternate_nick_get(PyIrcConnect *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->alternate_nick); +} + +/* Get/Set */ +static PyGetSetDef PyIrcConnect_getseters[] = { + {"alternate_nick", (getter)PyIrcConnect_alternate_nick_get, NULL, + PyIrcConnect_alternate_nick_doc, NULL}, + {NULL} +}; + +PyTypeObject PyIrcConnectType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "IrcConnect", /*tp_name*/ + sizeof(PyIrcConnect), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyIrcConnect objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + PyIrcConnect_getseters, /* tp_getset */ + &PyConnectType, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + +PyObject *pyirc_connect_new(void *connect, int managed) +{ + return pyconnect_sub_new(connect, &PyIrcConnectType, managed); +} + +int irc_connect_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyIrcConnectType) < 0) + return 0; + + Py_INCREF(&PyIrcConnectType); + PyModule_AddObject(py_module, "IrcConnect", (PyObject *)&PyIrcConnectType); + + return 1; +} diff --git a/src/objects/irc-connect-object.h b/src/objects/irc-connect-object.h new file mode 100644 index 0000000..3f6cad4 --- /dev/null +++ b/src/objects/irc-connect-object.h @@ -0,0 +1,21 @@ +#ifndef _IRC_CONNECT_OBJECT_H_ +#define _IRC_CONNECT_OBJECT_H_ + +#include <Python.h> +#include "connect-object.h" + +/* forward */ +struct _IRC_SERVER_CONNECT_REC; + +typedef struct +{ + PyIrssi_HEAD(struct _IRC_SERVER_CONNECT_REC) +} PyIrcConnect; + +extern PyTypeObject PyIrcConnectType; + +int irc_connect_object_init(void); +PyObject *pyirc_connect_new(void *connect, int managed); +#define pyirc_connect_check(op) PyObject_TypeCheck(op, &PyIrcConnectType) + +#endif diff --git a/src/objects/irc-server-object.c b/src/objects/irc-server-object.c new file mode 100644 index 0000000..4e9a28e --- /dev/null +++ b/src/objects/irc-server-object.c @@ -0,0 +1,491 @@ +#include <Python.h> +#include "pymodule.h" +#include "base-objects.h" +#include "irc-server-object.h" +#include "factory.h" +#include "pyirssi_irc.h" +#include "pycore.h" +#include "pyutils.h" + +/* cleanup and dealloc inherited from base Server */ + +/* Getters */ +PyDoc_STRVAR(PyIrcServer_real_address_doc, + "Address the IRC server gives" +); +static PyObject *PyIrcServer_real_address_get(PyIrcServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->real_address); +} + +PyDoc_STRVAR(PyIrcServer_usermode_doc, + "User mode in server" +); +static PyObject *PyIrcServer_usermode_get(PyIrcServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->usermode); +} + +PyDoc_STRVAR(PyIrcServer_userhost_doc, + "Your user host in server" +); +static PyObject *PyIrcServer_userhost_get(PyIrcServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->userhost); +} + +static PyGetSetDef PyIrcServer_getseters[] = { + {"real_address", (getter)PyIrcServer_real_address_get, NULL, + PyIrcServer_real_address_doc, NULL}, + {"usermode", (getter)PyIrcServer_usermode_get, NULL, + PyIrcServer_usermode_doc, NULL}, + {"userhost", (getter)PyIrcServer_userhost_get, NULL, + PyIrcServer_userhost_doc, NULL}, + {NULL} +}; + +/* Methods */ +PyDoc_STRVAR(get_channels_doc, + "get_channels() -> str\n" + "\n" + "Return a string of all channels (and keys, if any have them) in server,\n" + "like '#a,#b,#c,#d x,b_chan_key,x,x' or just '#e,#f,#g'\n" +); +static PyObject *PyIrcServer_get_channels(PyIrcServer *self, PyObject *args) +{ + char *list; + PyObject *ret; + + RET_NULL_IF_INVALID(self->data); + + list = irc_server_get_channels(self->data); + ret = PyString_FromString(list); + g_free(list); + + return ret; +} + +PyDoc_STRVAR(send_raw_doc, + "send_raw(cmd) -> None\n" + "\n" + "Send raw message to server, it will be flood protected so you\n" + "don't need to worry about it.\n" +); +static PyObject *PyIrcServer_send_raw(PyIrcServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"cmd", NULL}; + char *cmd; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &cmd)) + return NULL; + + irc_send_cmd(self->data, cmd); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(send_raw_now_doc, + "send_raw_now(cmd) -> None\n" + "\n" + "Send raw message to server immediately without flood protection.\n" +); +static PyObject *PyIrcServer_send_raw_now(PyIrcServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"cmd", NULL}; + char *cmd; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &cmd)) + return NULL; + + irc_send_cmd_now(self->data, cmd); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(send_raw_split_doc, + "send_raw_split(cmd, nickarg, max_nicks) -> None\n" + "\n" + "Split the `cmd' into several commands so `nickarg' argument has only\n" + "`max_nicks' number of nicks.\n" + "\n" + "Example:\n" + "server.send_raw_split('KICK #channel nick1,nick2,nick3 :byebye', 3, 2)\n" + "\n" + "Irssi will send commands 'KICK #channel nick1,nick2 :byebye' and\n" + "'KICK #channel nick3 :byebye' to server.\n" +); +static PyObject *PyIrcServer_send_raw_split(PyIrcServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"cmd", "nickarg", "max_nicks", NULL}; + char *cmd; + int nickarg; + int max_nicks; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "sii", kwlist, &cmd, &nickarg, &max_nicks)) + return NULL; + + irc_send_cmd_split(self->data, cmd, nickarg, max_nicks); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(ctcp_send_reply_doc, + "ctcp_send_reply(data) -> None\n" + "\n" + "Send CTCP reply. This will be 'CTCP flood protected' so if there's too\n" + "many CTCP requests in buffer, this reply might not get sent. The data\n" + "is the full raw command to be sent to server, like\n" + "'NOTICE nick :\001VERSION irssi\001'\n" +); +static PyObject *PyIrcServer_ctcp_send_reply(PyIrcServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"data", NULL}; + char *data; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &data)) + return NULL; + + ctcp_send_reply(self->data, data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(isupport_doc, + "isupport(name) -> str or None\n" + "\n" + "Returns the value of the named item in the ISUPPORT (005) numeric to the\n" + "script. If the item is not present returns undef, if the item has no value\n" + "then '' is returned use defined server.isupport('name') if you need to\n" + "check whether a property is present.\n" + "See http://www.ietf.org/internet-drafts/draft-brocklesby-irc-isupport-01.txt\n" + "for more information on the ISUPPORT numeric.\n" +); +static PyObject *PyIrcServer_isupport(PyIrcServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"name", NULL}; + char *name; + char *found; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &name)) + return NULL; + + found = g_hash_table_lookup(self->data->isupport, name); + + RET_AS_STRING_OR_NONE(found); +} + +PyDoc_STRVAR(PyIrcServer_netsplit_find_doc, + "netsplit_find(nick, address) -> Netsplit object or None\n" + "\n" + "Check if nick!address is on the other side of netsplit. Netsplit records\n" + "are automatically removed after 30 minutes (current default)..\n" +); +static PyObject *PyIrcServer_netsplit_find(PyIrcServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"nick", "address", NULL}; + char *nick = ""; + char *address = ""; + NETSPLIT_REC *ns; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist, + &nick, &address)) + return NULL; + + ns = netsplit_find(self->data, nick, address); + if (ns) + return pynetsplit_new(ns); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyIrcServer_netsplit_find_channel_doc, + "netsplit_find_channel(nick, address, channel) -> NetsplitChannel object or None\n" + "\n" + "Find nick record for nick!address in channel `channel'.\n" +); +static PyObject *PyIrcServer_netsplit_find_channel(PyIrcServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"nick", "address", "channel", NULL}; + char *nick = ""; + char *address = ""; + char *channel = ""; + NETSPLIT_CHAN_REC *nsc; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "sss", kwlist, + &nick, &address, &channel)) + return NULL; + + nsc = netsplit_find_channel(self->data, nick, address, channel); + if (nsc) + return pynetsplit_channel_new(nsc); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyIrcServer_notifylist_ison_doc, + "notifylist_ison(nick) -> bool\n" + "\n" + "Check if nick is on server\n" +); +static PyObject *PyIrcServer_notifylist_ison(PyIrcServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"nick", NULL}; + char *nick = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &nick)) + return NULL; + + return PyBool_FromLong(notifylist_ison_server(self->data, nick)); +} + +/* expect a list of tuples [('str', 'str'), ...] */ +static GSList *py_event_conv(PyObject *list) +{ + int i; + GSList *ret = NULL; + + if (!PyList_Check(list)) + { + PyErr_Format(PyExc_TypeError, "expect a list of tuples of two strings"); + return NULL; + } + + for (i = 0; i < PyList_Size(list); i++) + { + char *key; + char *val; + PyObject *tup = PyList_GET_ITEM(list, i); + + if (!PyTuple_Check(tup) || !PyArg_ParseTuple(tup, "ss", &key, &val)) + { + GSList *node; + + for (node = ret; node; node = node->next) + g_free(node->data); + + g_slist_free(ret); + + if (!PyErr_Occurred() || PyErr_ExceptionMatches(PyExc_TypeError)) + { + PyErr_Clear(); + PyErr_SetString(PyExc_TypeError, "expect a list of tuples of two strings"); + } + + return NULL; + } + + ret = g_slist_append(ret, g_strdup(key)); + ret = g_slist_append(ret, g_strdup(val)); + } + + return ret; +} + +PyDoc_STRVAR(PyIrcServer_redirect_event_doc, + "redirect_event(command, signals, arg=None, count=1, remote=-1, failure_signal=None) -> None\n" + "\n" + "Specify that the next command sent to server will be redirected.\n" + "NOTE: This command MUST be called before sending the command to server.\n" + "\n" + "`command' - Name of the registered redirection that we're using.\n" + "\n" + "`count' - How many times to execute the redirection. Some commands may\n" + "send multiple stop events, like MODE #a,#b.\n" + "\n" + "`arg' - The argument to be compared in event strings. You can give multiple\n" + "arguments separated with space.\n" + "\n" + "`remote' - Specifies if the command is a remote command, -1 = use default.\n" + "\n" + "`failure_signal' - If irssi can't find the stop signal for the redirection,\n" + "this signal is called.\n" + "\n" + "`signals' - hash reference with \"event\" => \"redir signal\" entries.\n" + "If the event is "", all the events belonging to the redirection but not\n" + "specified here, will be sent there.\n" + "\n" + "Example:\n" + "\n" + "# ignore all events generated by whois query, except 311.\n" + "\n" + "server.redirect_event(\"whois\",\n" + " remote = 0,\n" + " arg = \"cras\",\n" + " signals = [\n" + " ('event 311', 'redir whois'),\n" + " ('', 'event empty') \n" + " ]\n" + ")\n" + "server.send_raw(\"WHOIS :cras\")\n" +); +static PyObject *PyIrcServer_redirect_event(PyIrcServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"command", "signals", "arg", "count", "remote", "failure_signal", NULL}; + char *command = ""; + int count = 1; + char *arg = NULL; + int remote = -1; + char *failure_signal = NULL; + PyObject *signals = NULL; + GSList *gsignals; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "sO|ziiz", kwlist, + &command, &signals, &arg, &count, &remote, &failure_signal)) + return NULL; + + gsignals = py_event_conv(signals); + if (!gsignals) + return NULL; + + server_redirect_event(self->data, command, count, arg, remote, failure_signal, gsignals); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyIrcServer_redirect_get_signal_doc, + "redirect_get_signal(event, args) -> str\n" +); +static PyObject *PyIrcServer_redirect_get_signal(PyIrcServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"event", "args", NULL}; + char *event = ""; + char *pargs = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist, + &event, &pargs)) + return NULL; + + RET_AS_STRING_OR_NONE(server_redirect_get_signal(self->data, event, pargs)); +} + +PyDoc_STRVAR(PyIrcServer_redirect_peek_signal_doc, + "redirect_peek_signal(event, args) -> str\n" +); +static PyObject *PyIrcServer_redirect_peek_signal(PyIrcServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"event", "args", NULL}; + char *event = ""; + char *pargs = ""; + int redirection; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist, + &event, &pargs)) + return NULL; + + RET_AS_STRING_OR_NONE(server_redirect_peek_signal(self->data, event, pargs, &redirection)); +} + +/* Methods for object */ +static PyMethodDef PyIrcServer_methods[] = { + {"get_channels", (PyCFunction)PyIrcServer_get_channels, METH_NOARGS, + get_channels_doc}, + {"send_raw", (PyCFunction)PyIrcServer_send_raw, METH_VARARGS | METH_KEYWORDS, + send_raw_doc}, + {"send_raw_now", (PyCFunction)PyIrcServer_send_raw_now, METH_VARARGS | METH_KEYWORDS, + send_raw_now_doc}, + {"send_raw_split", (PyCFunction)PyIrcServer_send_raw_split, METH_VARARGS | METH_KEYWORDS, + send_raw_split_doc}, + {"ctcp_send_reply", (PyCFunction)PyIrcServer_ctcp_send_reply, METH_VARARGS | METH_KEYWORDS, + ctcp_send_reply_doc}, + {"isupport", (PyCFunction)PyIrcServer_isupport, METH_VARARGS | METH_KEYWORDS, + isupport_doc}, + {"netsplit_find", (PyCFunction)PyIrcServer_netsplit_find, METH_VARARGS | METH_KEYWORDS, + PyIrcServer_netsplit_find_doc}, + {"netsplit_find_channel", (PyCFunction)PyIrcServer_netsplit_find_channel, METH_VARARGS | METH_KEYWORDS, + PyIrcServer_netsplit_find_channel_doc}, + {"notifylist_ison", (PyCFunction)PyIrcServer_notifylist_ison, METH_VARARGS | METH_KEYWORDS, + PyIrcServer_notifylist_ison_doc}, + {"redirect_event", (PyCFunction)PyIrcServer_redirect_event, METH_VARARGS | METH_KEYWORDS, + PyIrcServer_redirect_event_doc}, + {"redirect_get_signal", (PyCFunction)PyIrcServer_redirect_get_signal, METH_VARARGS | METH_KEYWORDS, + PyIrcServer_redirect_get_signal_doc}, + {"redirect_peek_signal", (PyCFunction)PyIrcServer_redirect_peek_signal, METH_VARARGS | METH_KEYWORDS, + PyIrcServer_redirect_peek_signal_doc}, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyIrcServerType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "IrcServer", /*tp_name*/ + sizeof(PyIrcServer), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyIrcServer objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyIrcServer_methods, /* tp_methods */ + 0, /* tp_members */ + PyIrcServer_getseters, /* tp_getset */ + &PyServerType, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + +PyObject *pyirc_server_new(void *server) +{ + return pyserver_sub_new(server, &PyIrcServerType); +} + +int irc_server_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyIrcServerType) < 0) + return 0; + + Py_INCREF(&PyIrcServerType); + PyModule_AddObject(py_module, "IrcServer", (PyObject *)&PyIrcServerType); + + return 1; +} diff --git a/src/objects/irc-server-object.h b/src/objects/irc-server-object.h new file mode 100644 index 0000000..b3eb76a --- /dev/null +++ b/src/objects/irc-server-object.h @@ -0,0 +1,21 @@ +#ifndef _IRC_SERVER_OBJECT_H_ +#define _IRC_SERVER_OBJECT_H_ + +#include <Python.h> +#include "server-object.h" + +/* forward */ +struct _IRC_SERVER_REC; + +typedef struct +{ + PyServer_HEAD(struct _IRC_SERVER_REC) +} PyIrcServer; + +extern PyTypeObject PyIrcServerType; + +int irc_server_object_init(void); +PyObject *pyirc_server_new(void *server); +#define pyirc_server_check(op) PyObject_TypeCheck(op, &PyIrcServerType) + +#endif diff --git a/src/objects/log-object.c b/src/objects/log-object.c new file mode 100644 index 0000000..b18f826 --- /dev/null +++ b/src/objects/log-object.c @@ -0,0 +1,477 @@ +#include <Python.h> +#include "pyirssi_irc.h" +#include "pymodule.h" +#include "log-object.h" +#include "factory.h" +#include "pycore.h" + +static LOG_ITEM_REC *find_item(LOG_REC *log, PyLogitem *item); +static void log_cleanup(LOG_REC *log); +static int logtype(int *type, int target, int window); + +/* find/convert a py log item */ +static LOG_ITEM_REC *find_item(LOG_REC *log, PyLogitem *item) +{ + int type; + char *name; + char *servertag = NULL; + + if (!item->type || !item->name) + return NULL; + + type = PyInt_AS_LONG(item->type); + name = PyString_AS_STRING(item->name); + if (item->servertag) + servertag = PyString_AS_STRING(item->servertag); + + return log_item_find(log, type, name, servertag); +} + +/* monitor "log remove" signal */ +static void log_cleanup(LOG_REC *log) +{ + PyLog *pylog = signal_get_user_data(); + + if (log == pylog->data) + { + pylog->data = NULL; + pylog->cleanup_installed = 0; + signal_remove_data("log remove", log_cleanup, pylog); + } +} + +static void PyLog_dealloc(PyLog *self) +{ + if (self->cleanup_installed) + signal_remove_data("log remove", log_cleanup, self); + + if (self->data && !g_slist_find(logs, self->data)) + { + printtext(NULL, NULL, MSGLEVEL_CRAP, "destroying orphan log %s", self->data->fname); + log_close(self->data); + } + + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyLog_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyLog *self; + + self = (PyLog *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* function to create the log */ +PyDoc_STRVAR(PyLog_doc, + "__init__(fname, level=MSGLEVEL_ALL)\n" + "\n" + "Create a log\n" +); +static int PyLog_init(PyLog *self, PyObject *args, PyObject *kwds) +{ + char *fname; + int level = MSGLEVEL_ALL; + LOG_REC *log; + + static char *kwlist[] = {"fname", "level", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, + &fname, &level)) + return -1; + + /*XXX: anything better than RuntimeError ? */ + if (self->data || self->cleanup_installed) + { + PyErr_Format(PyExc_RuntimeError, "log already opened; close it first"); + return -1; + } + + log = log_create_rec(fname, level); + if (!log) + { + PyErr_Format(PyExc_RuntimeError, "failed to create log"); + return -1; + } + + self->data = log; + self->cleanup_installed = 1; + signal_add_last_data("log remove", log_cleanup, self); + + return 0; +} + +/* Getters */ +PyDoc_STRVAR(PyLog_fname_doc, + "Log file name" +); +static PyObject *PyLog_fname_get(PyLog *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->fname); +} + +PyDoc_STRVAR(PyLog_real_fname_doc, + "The actual opened log file (after %d.%m.Y etc. are expanded)" +); +static PyObject *PyLog_real_fname_get(PyLog *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->real_fname); +} + +PyDoc_STRVAR(PyLog_opened_doc, + "Log file is open" +); +static PyObject *PyLog_opened_get(PyLog *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromUnsignedLong(self->data->opened); +} + +PyDoc_STRVAR(PyLog_level_doc, + "Log only these levels" +); +static PyObject *PyLog_level_get(PyLog *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->level); +} + +PyDoc_STRVAR(PyLog_last_doc, + "Timestamp when last message was written" +); +static PyObject *PyLog_last_get(PyLog *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromUnsignedLong(self->data->last); +} + +PyDoc_STRVAR(PyLog_autoopen_doc, + "Automatically open log at startup" +); +static PyObject *PyLog_autoopen_get(PyLog *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->autoopen); +} + +PyDoc_STRVAR(PyLog_failed_doc, + "Opening log failed last time" +); +static PyObject *PyLog_failed_get(PyLog *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->failed); +} + +PyDoc_STRVAR(PyLog_temp_doc, + "Log isn't saved to config file" +); +static PyObject *PyLog_temp_get(PyLog *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->temp); +} + +/* specialized getters/setters */ +static PyGetSetDef PyLog_getseters[] = { + {"fname", (getter)PyLog_fname_get, NULL, + PyLog_fname_doc, NULL}, + {"real_fname", (getter)PyLog_real_fname_get, NULL, + PyLog_real_fname_doc, NULL}, + {"opened", (getter)PyLog_opened_get, NULL, + PyLog_opened_doc, NULL}, + {"level", (getter)PyLog_level_get, NULL, + PyLog_level_doc, NULL}, + {"last", (getter)PyLog_last_get, NULL, + PyLog_last_doc, NULL}, + {"autoopen", (getter)PyLog_autoopen_get, NULL, + PyLog_autoopen_doc, NULL}, + {"failed", (getter)PyLog_failed_get, NULL, + PyLog_failed_doc, NULL}, + {"temp", (getter)PyLog_temp_get, NULL, + PyLog_temp_doc, NULL}, + {NULL} +}; + +/* Methods */ +PyDoc_STRVAR(PyLog_items_doc, + "items() -> list of Log objects\n" + "\n" + "Return a list of log items\n" +); +static PyObject *PyLog_items(PyLog *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + return py_irssi_objlist_new(self->data->items, 1, (InitFunc)pylogitem_new); +} + +PyDoc_STRVAR(PyLog_update_doc, + "update() -> None\n" + "\n" + "Add log to list of logs / save changes to config file.\n" +); +static PyObject *PyLog_update(PyLog *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + log_update(self->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyLog_close_doc, + "destroy() -> None\n" + "\n" + "Destroy the log file\n" +); +static PyObject *PyLog_close(PyLog *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + log_close(self->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyLog_start_logging_doc, + "start_logging() -> None\n" + "\n" + "Open log file and start logging.\n" +); +static PyObject *PyLog_start_logging(PyLog *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + log_start_logging(self->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyLog_stop_logging_doc, + "stop_logging() -> None\n" + "\n" + "Stop and close the log file.\n" +); +static PyObject *PyLog_stop_logging(PyLog *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + log_stop_logging(self->data); + + Py_RETURN_NONE; +} + +static int logtype(int *type, int target, int window) +{ + if (target || window) + { + if (target && window) + { + PyErr_SetString(PyExc_TypeError, "must specify target or window, not both"); + return 0; + } + + *type = target? 0 : 1; + } + else if (*type < 0) + { + PyErr_SetString(PyExc_TypeError, "must specify type, target, or window"); + return 0; + } + + return 1; +} + +PyDoc_STRVAR(PyLog_item_add_doc, + "item_add(item, servertag=None, type=0, target=False, window=False) -> None\n" + "\n" + "Add a log item to log.\n" + "\n" + "Add a target item (nick, chan): \n" + " item_add('#linux', target=True)\n" + " item_add('#linux', type=0)\n" + "\n" + "Add a window ref: \n" + " item_add('2', window=True)\n" + " item_add('2', type=1)\n" +); +static PyObject *PyLog_item_add(PyLog *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"item", "servertag", "type", "target", "window", NULL}; + char *item = ""; + char *servertag = NULL; + int type = 0; + int target = 0; + int window = 0; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|ziii", kwlist, + &item, &servertag, &type, &target, &window)) + return NULL; + + if (!logtype(&type, target, window)) + return NULL; + + log_item_add(self->data, type, item, servertag); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyLog_item_destroy_doc, + "item_destroy(item) -> None\n" + "\n" + "Remove log item from log.\n" +); +static PyObject *PyLog_item_destroy(PyLog *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"item", NULL}; + PyObject *item = NULL; + LOG_ITEM_REC *li; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, + &item)) + return NULL; + + if (!pylogitem_check(item)) + return PyErr_Format(PyExc_TypeError, "arg 1 should be log item"); + + li = find_item(self->data, (PyLogitem *)item); + if (!li) + return PyErr_Format(PyExc_TypeError, "log item invalid or not found"); + + log_item_destroy(self->data, li); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyLog_item_find_doc, + "item_find(item, servertag=None, type=-1, target=False, window=False) -> item or None\n" + "\n" + "Find item from log.\n" +); +static PyObject *PyLog_item_find(PyLog *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"item", "servertag", "type", "target", "window", NULL}; + char *item = ""; + char *server = NULL; + int type = 0; + int target = 0; + int window = 0; + LOG_ITEM_REC *li; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|ziii", kwlist, + &item, &server, &type, &target, &window)) + return NULL; + + if (!logtype(&type, target, window)) + return NULL; + + li = log_item_find(self->data, type, item, server); + if (li) + return pylogitem_new(li); + + Py_RETURN_NONE; +} + +/* Methods for object */ +static PyMethodDef PyLog_methods[] = { + {"items", (PyCFunction)PyLog_items, METH_NOARGS, + PyLog_items_doc}, + {"update", (PyCFunction)PyLog_update, METH_NOARGS, + PyLog_update_doc}, + {"close", (PyCFunction)PyLog_close, METH_NOARGS, + PyLog_close_doc}, + {"start_logging", (PyCFunction)PyLog_start_logging, METH_NOARGS, + PyLog_start_logging_doc}, + {"stop_logging", (PyCFunction)PyLog_stop_logging, METH_NOARGS, + PyLog_stop_logging_doc}, + {"item_add", (PyCFunction)PyLog_item_add, METH_VARARGS | METH_KEYWORDS, + PyLog_item_add_doc}, + {"item_destroy", (PyCFunction)PyLog_item_destroy, METH_VARARGS | METH_KEYWORDS, + PyLog_item_destroy_doc}, + {"item_find", (PyCFunction)PyLog_item_find, METH_VARARGS | METH_KEYWORDS, + PyLog_item_find_doc}, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyLogType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Log", /*tp_name*/ + sizeof(PyLog), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyLog_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + PyLog_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyLog_methods, /* tp_methods */ + 0, /* tp_members */ + PyLog_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)PyLog_init, /* tp_init */ + 0, /* tp_alloc */ + PyLog_new, /* tp_new */ +}; + + +/* window item wrapper factory function */ +PyObject *pylog_new(void *log) +{ + PyLog *pylog; + + pylog = (PyLog *)PyLogType.tp_alloc(&PyLogType, 0); + if (!pylog) + return NULL; + + pylog->data = log; + pylog->cleanup_installed = 1; + signal_add_last_data("log remove", log_cleanup, pylog); + + return (PyObject *)pylog; +} + +int log_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyLogType) < 0) + return 0; + + Py_INCREF(&PyLogType); + PyModule_AddObject(py_module, "Log", (PyObject *)&PyLogType); + + return 1; +} diff --git a/src/objects/log-object.h b/src/objects/log-object.h new file mode 100644 index 0000000..9893bc5 --- /dev/null +++ b/src/objects/log-object.h @@ -0,0 +1,21 @@ +#ifndef _LOG_OBJECT_H_ +#define _LOG_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +/* forward */ +struct _LOG_REC; + +typedef struct +{ + PyIrssiFinal_HEAD(struct _LOG_REC) +} PyLog; + +extern PyTypeObject PyLogType; + +int log_object_init(void); +PyObject *pylog_new(void *log); +#define pylog_check(op) PyObject_TypeCheck(op, &PyLogType) + +#endif diff --git a/src/objects/logitem-object.c b/src/objects/logitem-object.c new file mode 100644 index 0000000..4dc2275 --- /dev/null +++ b/src/objects/logitem-object.c @@ -0,0 +1,156 @@ +#include <Python.h> +#include "pyirssi_irc.h" +#include "pymodule.h" +#include "logitem-object.h" +#include "pycore.h" + +/* no special cleanup -- value copy is made */ + +static void PyLogitem_dealloc(PyLogitem *self) +{ + Py_XDECREF(self->type); + Py_XDECREF(self->name); + Py_XDECREF(self->servertag); + + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyLogitem_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyLogitem *self; + + self = (PyLogitem *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* Getters */ +PyDoc_STRVAR(PyLogitem_type_doc, + "0=target, 1=window refnum" +); +static PyObject *PyLogitem_type_get(PyLogitem *self, void *closure) +{ + RET_AS_OBJ_OR_NONE(self->type); +} + +PyDoc_STRVAR(PyLogitem_name_doc, + "Name" +); +static PyObject *PyLogitem_name_get(PyLogitem *self, void *closure) +{ + RET_AS_OBJ_OR_NONE(self->name); +} + +PyDoc_STRVAR(PyLogitem_servertag_doc, + "Server tag" +); +static PyObject *PyLogitem_servertag_get(PyLogitem *self, void *closure) +{ + RET_AS_OBJ_OR_NONE(self->servertag); +} + +/* specialized getters/setters */ +static PyGetSetDef PyLogitem_getseters[] = { + {"type", (getter)PyLogitem_type_get, NULL, + PyLogitem_type_doc, NULL}, + {"name", (getter)PyLogitem_name_get, NULL, + PyLogitem_name_doc, NULL}, + {"servertag", (getter)PyLogitem_servertag_get, NULL, + PyLogitem_servertag_doc, NULL}, + {NULL} +}; + +/* Methods for object */ +static PyMethodDef PyLogitem_methods[] = { + {NULL} /* Sentinel */ +}; + +PyTypeObject PyLogitemType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Logitem", /*tp_name*/ + sizeof(PyLogitem), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyLogitem_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyLogitem objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyLogitem_methods, /* tp_methods */ + 0, /* tp_members */ + PyLogitem_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyLogitem_new, /* tp_new */ +}; + + +/* log item factory function */ +PyObject *pylogitem_new(void *log) +{ + LOG_ITEM_REC *li = log; + PyLogitem *pylog = NULL; + + pylog = py_inst(PyLogitem, PyLogitemType); + if (!pylog) + return NULL; + + pylog->type = PyInt_FromLong(li->type); + if (!pylog->type) + goto error; + + pylog->name = PyString_FromString(li->name); + if (!pylog->name) + goto error; + + if (li->servertag) + { + pylog->servertag = PyString_FromString(li->servertag); + if (!pylog->servertag) + goto error; + } + + return (PyObject *)pylog; + +error: + Py_XDECREF(pylog); + return NULL; +} + +int logitem_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyLogitemType) < 0) + return 0; + + Py_INCREF(&PyLogitemType); + PyModule_AddObject(py_module, "Logitem", (PyObject *)&PyLogitemType); + + return 1; +} diff --git a/src/objects/logitem-object.h b/src/objects/logitem-object.h new file mode 100644 index 0000000..b7fd588 --- /dev/null +++ b/src/objects/logitem-object.h @@ -0,0 +1,21 @@ +#ifndef _LOG_ITEM_OBJECT_H_ +#define _LOG_ITEM_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +typedef struct +{ + PyObject_HEAD + PyObject *type; + PyObject *name; + PyObject *servertag; +} PyLogitem; + +extern PyTypeObject PyLogitemType; + +int logitem_object_init(void); +PyObject *pylogitem_new(void *log); +#define pylogitem_check(op) PyObject_TypeCheck(op, &PyLogitemType) + +#endif diff --git a/src/objects/main-window-object.c b/src/objects/main-window-object.c new file mode 100644 index 0000000..6541f2b --- /dev/null +++ b/src/objects/main-window-object.c @@ -0,0 +1,199 @@ +#include <Python.h> +#include "pyirssi.h" +#include "pymodule.h" +#include "main-window-object.h" +#include "factory.h" +#include "pycore.h" + +#define MW(data) ((MAIN_WINDOW_REC *) data) + +/* monitor "mainwindow destroyed" signal */ +static void main_window_cleanup(MAIN_WINDOW_REC *mw) +{ + PyMainWindow *pymw = signal_get_user_data(); + + if (mw == pymw->data) + { + pymw->data = NULL; + pymw->cleanup_installed = 0; + signal_remove_data("mainwindow destroyed", main_window_cleanup, pymw); + } +} + +static void PyMainWindow_dealloc(PyMainWindow *self) +{ + if (self->cleanup_installed) + signal_remove_data("mainwindow destroyed", main_window_cleanup, self); + + Py_XDECREF(self->active); + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyMainWindow_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyMainWindow *self; + + self = (PyMainWindow *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* getters */ +PyDoc_STRVAR(PyMainWindow_active_doc, + "active window object" +); +static PyObject *PyMainWindow_active_get(PyMainWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_OBJ_OR_NONE(self->active); +} + +PyDoc_STRVAR(PyMainWindow_first_line_doc, + "first line used by this window (0..x) (includes statusbars)" +); +static PyObject *PyMainWindow_first_line_get(PyMainWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(MW(self->data)->first_line); +} + +PyDoc_STRVAR(PyMainWindow_last_line_doc, + "last line used by this window (0..x) (includes statusbars)" +); +static PyObject *PyMainWindow_last_line_get(PyMainWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(MW(self->data)->last_line); +} + +PyDoc_STRVAR(PyMainWindow_width_doc, + "width of the window (includes statusbars)" +); +static PyObject *PyMainWindow_width_get(PyMainWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(MW(self->data)->width); +} + +PyDoc_STRVAR(PyMainWindow_height_doc, + "height of the window (includes statusbars)" +); +static PyObject *PyMainWindow_height_get(PyMainWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(MW(self->data)->height); +} + +PyDoc_STRVAR(PyMainWindow_statusbar_lines_doc, + "???" +); +static PyObject *PyMainWindow_statusbar_lines_get(PyMainWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(MW(self->data)->statusbar_lines); +} + +/* specialized getters/setters */ +static PyGetSetDef PyMainWindow_getseters[] = { + {"active", (getter)PyMainWindow_active_get, NULL, + PyMainWindow_active_doc, NULL}, + {"first_line", (getter)PyMainWindow_first_line_get, NULL, + PyMainWindow_first_line_doc, NULL}, + {"last_line", (getter)PyMainWindow_last_line_get, NULL, + PyMainWindow_last_line_doc, NULL}, + {"width", (getter)PyMainWindow_width_get, NULL, + PyMainWindow_width_doc, NULL}, + {"height", (getter)PyMainWindow_height_get, NULL, + PyMainWindow_height_doc, NULL}, + {"statusbar_lines", (getter)PyMainWindow_statusbar_lines_get, NULL, + PyMainWindow_statusbar_lines_doc, NULL}, + {NULL} +}; + +/* Methods for object */ +static PyMethodDef PyMainWindow_methods[] = { + {NULL} /* Sentinel */ +}; + +PyTypeObject PyMainWindowType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "MainWindow", /*tp_name*/ + sizeof(PyMainWindow), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyMainWindow_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyMainWindow objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyMainWindow_methods, /* tp_methods */ + 0, /* tp_members */ + PyMainWindow_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyMainWindow_new, /* tp_new */ +}; + + +/* main window wrapper factory function */ +PyObject *pymain_window_new(MAIN_WINDOW_REC *mw) +{ + PyObject *pyactive = NULL; + PyMainWindow *pymw; + + pyactive = pywindow_new(mw->active); + if (!pyactive) + return NULL; + + pymw = py_inst(PyMainWindow, PyMainWindowType); + if (!pymw) + { + Py_DECREF(pyactive); + return NULL; + } + + pymw->active = pyactive; + pymw->data = mw; + pymw->cleanup_installed = 1; + signal_add_last_data("mainwindow destroyed", main_window_cleanup, pymw); + + return (PyObject *)pymw; +} + +int main_window_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyMainWindowType) < 0) + return 0; + + Py_INCREF(&PyMainWindowType); + PyModule_AddObject(py_module, "MainWindow", (PyObject *)&PyMainWindowType); + + return 1; +} diff --git a/src/objects/main-window-object.h b/src/objects/main-window-object.h new file mode 100644 index 0000000..fbbdac0 --- /dev/null +++ b/src/objects/main-window-object.h @@ -0,0 +1,20 @@ +#ifndef _MAIN_WINDOW_OBJECT_H_ +#define _MAIN_WINDOW_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" +#include "pyirssi.h" + +typedef struct +{ + PyIrssiFinal_HEAD(void) + PyObject *active; +} PyMainWindow; + +extern PyTypeObject PyMainWindowType; + +int main_window_object_init(void); +PyObject *pymain_window_new(MAIN_WINDOW_REC *mw); +#define pymain_window_check(op) PyObject_TypeCheck(op, &PyMainWindowType) + +#endif diff --git a/src/objects/netsplit-channel-object.c b/src/objects/netsplit-channel-object.c new file mode 100644 index 0000000..3e2639b --- /dev/null +++ b/src/objects/netsplit-channel-object.c @@ -0,0 +1,170 @@ +#include <Python.h> +#include "pyirssi_irc.h" +#include "pymodule.h" +#include "netsplit-channel-object.h" +#include "factory.h" +#include "pycore.h" + +/* value copied -- no special cleanup */ + +static void PyNetsplitChannel_dealloc(PyNetsplitChannel *self) +{ + Py_XDECREF(self->name); + + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyNetsplitChannel_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyNetsplitChannel *self; + + self = (PyNetsplitChannel *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* Getters */ +PyDoc_STRVAR(PyNetsplitChannel_name_doc, + "Channel name" +); +static PyObject *PyNetsplitChannel_name_get(PyNetsplitChannel *self, void *closure) +{ + RET_AS_OBJ_OR_NONE(self->name); +} + +PyDoc_STRVAR(PyNetsplitChannel_op_doc, + "is op" +); +static PyObject *PyNetsplitChannel_op_get(PyNetsplitChannel *self, void *closure) +{ + return PyBool_FromLong(self->op); +} + +PyDoc_STRVAR(PyNetsplitChannel_halfop_doc, + "is halfop" +); +static PyObject *PyNetsplitChannel_halfop_get(PyNetsplitChannel *self, void *closure) +{ + return PyBool_FromLong(self->halfop); +} + +PyDoc_STRVAR(PyNetsplitChannel_voice_doc, + "is voice" +); +static PyObject *PyNetsplitChannel_voice_get(PyNetsplitChannel *self, void *closure) +{ + return PyBool_FromLong(self->voice); +} + +PyDoc_STRVAR(PyNetsplitChannel_other_doc, + "?" +); +static PyObject *PyNetsplitChannel_other_get(PyNetsplitChannel *self, void *closure) +{ + return PyInt_FromLong(self->other); +} + +/* specialized getters/setters */ +static PyGetSetDef PyNetsplitChannel_getseters[] = { + {"name", (getter)PyNetsplitChannel_name_get, NULL, + PyNetsplitChannel_name_doc, NULL}, + {"op", (getter)PyNetsplitChannel_op_get, NULL, + PyNetsplitChannel_op_doc, NULL}, + {"halfop", (getter)PyNetsplitChannel_halfop_get, NULL, + PyNetsplitChannel_halfop_doc, NULL}, + {"voice", (getter)PyNetsplitChannel_voice_get, NULL, + PyNetsplitChannel_voice_doc, NULL}, + {"other", (getter)PyNetsplitChannel_other_get, NULL, + PyNetsplitChannel_other_doc, NULL}, + {NULL} +}; + +/* Methods */ +/* Methods for object */ +static PyMethodDef PyNetsplitChannel_methods[] = { + {NULL} /* Sentinel */ +}; + +PyTypeObject PyNetsplitChannelType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "NetsplitChannel", /*tp_name*/ + sizeof(PyNetsplitChannel), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyNetsplitChannel_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyNetsplitChannel objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyNetsplitChannel_methods, /* tp_methods */ + 0, /* tp_members */ + PyNetsplitChannel_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyNetsplitChannel_new, /* tp_new */ +}; + + +/* window item wrapper factory function */ +PyObject *pynetsplit_channel_new(void *netsplit) +{ + NETSPLIT_CHAN_REC *rec = netsplit; + PyNetsplitChannel *pynetsplit; + PyObject *name; + + name = PyString_FromString(rec->name); + if (!name) + return NULL; + + pynetsplit = py_inst(PyNetsplitChannel, PyNetsplitChannelType); + if (!pynetsplit) + { + Py_DECREF(name); + return NULL; + } + + pynetsplit->name = name; + pynetsplit->op = rec->op; + pynetsplit->halfop = rec->halfop; + pynetsplit->other = rec->other; + + return (PyObject *)pynetsplit; +} + +int netsplit_channel_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyNetsplitChannelType) < 0) + return 0; + + Py_INCREF(&PyNetsplitChannelType); + PyModule_AddObject(py_module, "NetsplitChannel", (PyObject *)&PyNetsplitChannelType); + + return 1; +} diff --git a/src/objects/netsplit-channel-object.h b/src/objects/netsplit-channel-object.h new file mode 100644 index 0000000..3a00fbb --- /dev/null +++ b/src/objects/netsplit-channel-object.h @@ -0,0 +1,21 @@ +#ifndef _NETSPLIT_CHANNEL_OBJECT_H_ +#define _NETSPLIT_CHANNEL_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +typedef struct +{ + PyObject_HEAD + PyObject *name; + int op, halfop; + int voice, other; +} PyNetsplitChannel; + +extern PyTypeObject PyNetsplitChannelType; + +int netsplit_channel_object_init(void); +PyObject *pynetsplit_channel_new(void *ns); +#define pynetsplit_channel_check(op) PyObject_TypeCheck(op, &PyNetsplitChannelType) + +#endif diff --git a/src/objects/netsplit-object.c b/src/objects/netsplit-object.c new file mode 100644 index 0000000..d64bd97 --- /dev/null +++ b/src/objects/netsplit-object.c @@ -0,0 +1,184 @@ +#include <Python.h> +#include "pyirssi_irc.h" +#include "pymodule.h" +#include "netsplit-object.h" +#include "factory.h" +#include "pycore.h" + +#define NETSPLIT(ns) ((NETSPLIT_REC*)ns) + +/* monitor "netsplit remove" signal */ +static void netsplit_cleanup(NETSPLIT_REC *netsplit) +{ + PyNetsplit *pynetsplit = signal_get_user_data(); + + if (netsplit == pynetsplit->data) + { + pynetsplit->data = NULL; + pynetsplit->cleanup_installed = 0; + signal_remove_data("netsplit remove", netsplit_cleanup, pynetsplit); + } +} + +static void PyNetsplit_dealloc(PyNetsplit *self) +{ + if (self->cleanup_installed) + signal_remove_data("netsplit remove", netsplit_cleanup, self); + + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyNetsplit_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyNetsplit *self; + + self = (PyNetsplit *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* Getters */ +PyDoc_STRVAR(PyNetsplit_nick_doc, + "Nick" +); +static PyObject *PyNetsplit_nick_get(PyNetsplit *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(NETSPLIT(self->data)->nick); +} + +PyDoc_STRVAR(PyNetsplit_address_doc, + "Nick's host" +); +static PyObject *PyNetsplit_address_get(PyNetsplit *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(NETSPLIT(self->data)->address); +} + +PyDoc_STRVAR(PyNetsplit_destroy_doc, + "Timestamp when this record should be destroyed" +); +static PyObject *PyNetsplit_destroy_get(PyNetsplit *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromUnsignedLong(NETSPLIT(self->data)->destroy); +} + +PyDoc_STRVAR(PyNetsplit_server_doc, + "Netsplitserver object" +); +static PyObject *PyNetsplit_server_get(PyNetsplit *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_OBJ_OR_NONE(self->server); +} + +/* specialized getters/setters */ +static PyGetSetDef PyNetsplit_getseters[] = { + {"nick", (getter)PyNetsplit_nick_get, NULL, + PyNetsplit_nick_doc, NULL}, + {"address", (getter)PyNetsplit_address_get, NULL, + PyNetsplit_address_doc, NULL}, + {"destroy", (getter)PyNetsplit_destroy_get, NULL, + PyNetsplit_destroy_doc, NULL}, + {"server", (getter)PyNetsplit_server_get, NULL, + PyNetsplit_server_doc, NULL}, + {NULL} +}; + +/* Methods */ +PyDoc_STRVAR(PyNetsplit_channels_doc, + "channels() -> list of NetsplitChannel objects\n" + "\n" + "Return list of NetsplitChannel objects\n" +); +static PyObject *PyNetsplit_channels(PyNetsplit *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + return py_irssi_objlist_new(NETSPLIT(self->data)->channels, 1, + (InitFunc)pynetsplit_channel_new); +} + +/* Methods for object */ +static PyMethodDef PyNetsplit_methods[] = { + {"channels", (PyCFunction)PyNetsplit_channels, METH_NOARGS, + PyNetsplit_channels_doc}, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyNetsplitType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Netsplit", /*tp_name*/ + sizeof(PyNetsplit), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyNetsplit_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyNetsplit objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyNetsplit_methods, /* tp_methods */ + 0, /* tp_members */ + PyNetsplit_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyNetsplit_new, /* tp_new */ +}; + + +/* window item wrapper factory function */ +PyObject *pynetsplit_new(void *netsplit) +{ + PyNetsplit *pynetsplit; + + //FIXME: add netsplit server + + pynetsplit = py_inst(PyNetsplit, PyNetsplitType); + if (!pynetsplit) + return NULL; + + pynetsplit->data = netsplit; + pynetsplit->cleanup_installed = 1; + signal_add_last_data("netsplit remove", netsplit_cleanup, pynetsplit); + + return (PyObject *)pynetsplit; +} + +int netsplit_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyNetsplitType) < 0) + return 0; + + Py_INCREF(&PyNetsplitType); + PyModule_AddObject(py_module, "Netsplit", (PyObject *)&PyNetsplitType); + + return 1; +} diff --git a/src/objects/netsplit-object.h b/src/objects/netsplit-object.h new file mode 100644 index 0000000..bd166e0 --- /dev/null +++ b/src/objects/netsplit-object.h @@ -0,0 +1,19 @@ +#ifndef _NETSPLIT_OBJECT_H_ +#define _NETSPLIT_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +typedef struct +{ + PyIrssiFinal_HEAD(void) + PyObject *server; +} PyNetsplit; + +extern PyTypeObject PyNetsplitType; + +int netsplit_object_init(void); +PyObject *pynetsplit_new(void *ns); +#define pynetsplit_check(op) PyObject_TypeCheck(op, &PyNetsplitType) + +#endif diff --git a/src/objects/netsplit-server-object.c b/src/objects/netsplit-server-object.c new file mode 100644 index 0000000..7208ae1 --- /dev/null +++ b/src/objects/netsplit-server-object.c @@ -0,0 +1,157 @@ +#include <Python.h> +#include "pyirssi_irc.h" +#include "pymodule.h" +#include "netsplit-object.h" +#include "factory.h" +#include "pycore.h" + +#define NETSPLIT_SERVER(ns) ((NETSPLIT_SERVER_REC*)ns) + +/* monitor "netsplit remove" signal */ +static void netsplit_server_cleanup(NETSPLIT_SERVER_REC *netsplit) +{ + PyNetsplitServer *pynetsplit = signal_get_user_data(); + + if (netsplit == pynetsplit->data) + { + pynetsplit->data = NULL; + pynetsplit->cleanup_installed = 0; + signal_remove_data("netsplit remove", netsplit_server_cleanup, pynetsplit); + } +} + +static void PyNetsplitServer_dealloc(PyNetsplitServer *self) +{ + if (self->cleanup_installed) + signal_remove_data("netsplit remove", netsplit_server_cleanup, self); + + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyNetsplitServer_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyNetsplitServer *self; + + self = (PyNetsplitServer *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* Getters */ +PyDoc_STRVAR(PyNetsplitServer_server_doc, + "The server nick was in" +); +static PyObject *PyNetsplitServer_server_get(PyNetsplitServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(NETSPLIT_SERVER(self->data)->server); +} + +PyDoc_STRVAR(PyNetsplitServer_destserver_doc, + "The other server where split occured." +); +static PyObject *PyNetsplitServer_destserver_get(PyNetsplitServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(NETSPLIT_SERVER(self->data)->destserver); +} + +PyDoc_STRVAR(PyNetsplitServer_count_doc, + "Number of splits in server" +); +static PyObject *PyNetsplitServer_count_get(PyNetsplitServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(NETSPLIT_SERVER(self->data)->count); +} + +/* specialized getters/setters */ +static PyGetSetDef PyNetsplitServer_getseters[] = { + {"server", (getter)PyNetsplitServer_server_get, NULL, + PyNetsplitServer_server_doc, NULL}, + {"destserver", (getter)PyNetsplitServer_destserver_get, NULL, + PyNetsplitServer_destserver_doc, NULL}, + {"count", (getter)PyNetsplitServer_count_get, NULL, + PyNetsplitServer_count_doc, NULL}, + {NULL} +}; + +/* Methods */ +/* Methods for object */ +static PyMethodDef PyNetsplitServer_methods[] = { + {NULL} /* Sentinel */ +}; + +PyTypeObject PyNetsplitServerType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "NetsplitServer", /*tp_name*/ + sizeof(PyNetsplitServer), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyNetsplitServer_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyNetsplitServer objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyNetsplitServer_methods, /* tp_methods */ + 0, /* tp_members */ + PyNetsplitServer_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyNetsplitServer_new, /* tp_new */ +}; + + +/* window item wrapper factory function */ +PyObject *pynetsplit_server_new(void *nss) +{ + PyNetsplitServer *pynss; + + pynss = py_inst(PyNetsplitServer, PyNetsplitServerType); + if (!pynss) + return NULL; + + pynss->data = nss; + pynss->cleanup_installed = 1; + signal_add_last_data("netsplit server remove", netsplit_server_cleanup, pynss); + + return (PyObject *)pynss; +} + +int netsplit_server_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyNetsplitServerType) < 0) + return 0; + + Py_INCREF(&PyNetsplitServerType); + PyModule_AddObject(py_module, "NetsplitServer", (PyObject *)&PyNetsplitServerType); + + return 1; +} diff --git a/src/objects/netsplit-server-object.h b/src/objects/netsplit-server-object.h new file mode 100644 index 0000000..f1d5fbf --- /dev/null +++ b/src/objects/netsplit-server-object.h @@ -0,0 +1,18 @@ +#ifndef _NETSPLIT_SERVER_OBJECT_H_ +#define _NETSPLIT_SERVER_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +typedef struct +{ + PyIrssiFinal_HEAD(void) +} PyNetsplitServer; + +extern PyTypeObject PyNetsplitServerType; + +int netsplit_server_object_init(void); +PyObject *pynetsplit_server_new(void *nss); +#define pynetsplit_server_check(op) PyObject_TypeCheck(op, &PyNetsplitServerType) + +#endif diff --git a/src/objects/nick-object.c b/src/objects/nick-object.c new file mode 100644 index 0000000..79f3a85 --- /dev/null +++ b/src/objects/nick-object.c @@ -0,0 +1,237 @@ +#include <Python.h> +#include "pymodule.h" +#include "base-objects.h" +#include "nick-object.h" +#include "pyirssi.h" +#include "pycore.h" +#include "pyutils.h" + +static void nick_cleanup(CHANNEL_REC *chan, NICK_REC *nick) +{ + PyNick *pynick = signal_get_user_data(); + + if (nick == pynick->data) + { + pynick->data = NULL; + pynick->cleanup_installed = 0; + signal_remove_data("nicklist remove", nick_cleanup, pynick); + } +} + +static void PyNick_dealloc(PyNick *self) +{ + if (self->cleanup_installed) + signal_remove_data("nicklist remove", nick_cleanup, self); + + self->ob_type->tp_free((PyObject*)self); +} + +/* Getters */ +PyDoc_STRVAR(PyNick_send_massjoin_doc, + "Waiting to be sent in a 'massjoin' signal, True or False" +); +static PyObject *PyNick_send_massjoin_get(PyNick *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->send_massjoin); +} + +PyDoc_STRVAR(PyNick_nick_doc, + "Plain nick" +); +static PyObject *PyNick_nick_get(PyNick *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->nick); +} + +PyDoc_STRVAR(PyNick_host_doc, + "Host address" +); +static PyObject *PyNick_host_get(PyNick *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->host); +} + +PyDoc_STRVAR(PyNick_realname_doc, + "Real name" +); +static PyObject *PyNick_realname_get(PyNick *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->realname); +} + +PyDoc_STRVAR(PyNick_hops_doc, + "Hop count to the server the nick is using" +); +static PyObject *PyNick_hops_get(PyNick *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->hops); +} + +PyDoc_STRVAR(PyNick_gone_doc, + "User status" +); +static PyObject *PyNick_gone_get(PyNick *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->gone); +} + +PyDoc_STRVAR(PyNick_serverop_doc, + "User status" +); +static PyObject *PyNick_serverop_get(PyNick *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->serverop); +} + +PyDoc_STRVAR(PyNick_op_doc, + "User status" +); +static PyObject *PyNick_op_get(PyNick *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->op); +} + +PyDoc_STRVAR(PyNick_voice_doc, + "User status" +); +static PyObject *PyNick_voice_get(PyNick *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->voice); +} + +PyDoc_STRVAR(PyNick_halfop_doc, + "User status" +); +static PyObject *PyNick_halfop_get(PyNick *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->halfop); +} + +PyDoc_STRVAR(PyNick_last_check_doc, + "timestamp when last checked gone/ircop status." +); +static PyObject *PyNick_last_check_get(PyNick *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromUnsignedLong(self->data->last_check); +} + +/* specialized getters/setters */ +static PyGetSetDef PyNick_getseters[] = { + {"send_massjoin", (getter)PyNick_send_massjoin_get, NULL, + PyNick_send_massjoin_doc, NULL}, + {"nick", (getter)PyNick_nick_get, NULL, + PyNick_nick_doc, NULL}, + {"host", (getter)PyNick_host_get, NULL, + PyNick_host_doc, NULL}, + {"realname", (getter)PyNick_realname_get, NULL, + PyNick_realname_doc, NULL}, + {"hops", (getter)PyNick_hops_get, NULL, + PyNick_hops_doc, NULL}, + {"gone", (getter)PyNick_gone_get, NULL, + PyNick_gone_doc, NULL}, + {"serverop", (getter)PyNick_serverop_get, NULL, + PyNick_serverop_doc, NULL}, + {"op", (getter)PyNick_op_get, NULL, + PyNick_op_doc, NULL}, + {"voice", (getter)PyNick_voice_get, NULL, + PyNick_voice_doc, NULL}, + {"halfop", (getter)PyNick_halfop_get, NULL, + PyNick_halfop_doc, NULL}, + {"last_check", (getter)PyNick_last_check_get, NULL, + PyNick_last_check_doc, NULL}, + {NULL} +}; + +static PyMethodDef PyNick_methods[] = { + {NULL} /* Sentinel */ +}; + +PyTypeObject PyNickType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Nick", /*tp_name*/ + sizeof(PyNick), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyNick_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyNick objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyNick_methods, /* tp_methods */ + 0, /* tp_members */ + PyNick_getseters, /* tp_getset */ + &PyIrssiChatBaseType, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + + +/* nick factory function */ +PyObject *pynick_sub_new(void *nick, PyTypeObject *subclass) +{ + static const char *name = "NICK"; + PyNick *pynick = NULL; + + pynick = py_instp(PyNick, subclass); + if (!pynick) + return NULL; + + pynick->data = nick; + pynick->base_name = name; + signal_add_last_data("nicklist remove", nick_cleanup, pynick); + pynick->cleanup_installed = 1; + + return (PyObject *)pynick; +} + +PyObject *pynick_new(void *nick) +{ + return pynick_sub_new(nick, &PyNickType); +} + +int nick_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyNickType) < 0) + return 0; + + Py_INCREF(&PyNickType); + PyModule_AddObject(py_module, "Nick", (PyObject *)&PyNickType); + + return 1; +} diff --git a/src/objects/nick-object.h b/src/objects/nick-object.h new file mode 100644 index 0000000..6831994 --- /dev/null +++ b/src/objects/nick-object.h @@ -0,0 +1,22 @@ +#ifndef _NICK_OBJECT_H_ +#define _NICK_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +/* forward */ +struct _NICK_REC; + +typedef struct +{ + PyIrssi_HEAD(struct _NICK_REC) +} PyNick; + +extern PyTypeObject PyNickType; + +int nick_object_init(void); +PyObject *pynick_sub_new(void *nick, PyTypeObject *subclass); +PyObject *pynick_new(void *nick); +#define pynick_check(op) PyObject_TypeCheck(op, &PyNickType) + +#endif diff --git a/src/objects/notifylist-object.c b/src/objects/notifylist-object.c new file mode 100644 index 0000000..3366d0f --- /dev/null +++ b/src/objects/notifylist-object.c @@ -0,0 +1,221 @@ +#include <Python.h> +#include "pyirssi_irc.h" +#include "pymodule.h" +#include "notifylist-object.h" +#include "pycore.h" + +#define NOTIFYLIST(nl) ((NOTIFYLIST_REC *)nl) + +/* monitor "notifylist remove" signal */ +static void notifylist_cleanup(NOTIFYLIST_REC *notifylist) +{ + PyNotifylist *pynotifylist = signal_get_user_data(); + + if (notifylist == pynotifylist->data) + { + pynotifylist->data = NULL; + pynotifylist->cleanup_installed = 0; + signal_remove_data("notifylist remove", notifylist_cleanup, pynotifylist); + } +} + +static void PyNotifylist_dealloc(PyNotifylist *self) +{ + if (self->cleanup_installed) + signal_remove_data("notifylist remove", notifylist_cleanup, self); + + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyNotifylist_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyNotifylist *self; + + self = (PyNotifylist *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* Getters */ +PyDoc_STRVAR(PyNotifylist_mask_doc, + "Notify nick mask" +); +static PyObject *PyNotifylist_mask_get(PyNotifylist *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(NOTIFYLIST(self->data)->mask); +} + +PyDoc_STRVAR(PyNotifylist_away_check_doc, + "Notify away status changes" +); +static PyObject *PyNotifylist_away_check_get(PyNotifylist *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(NOTIFYLIST(self->data)->away_check); +} + +PyDoc_STRVAR(PyNotifylist_idle_check_time_doc, + "Notify when idle time is reset and idle was bigger than this (seconds)" +); +static PyObject *PyNotifylist_idle_check_time_get(PyNotifylist *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromUnsignedLong(NOTIFYLIST(self->data)->idle_check_time); +} + +/* specialized getters/setters */ +static PyGetSetDef PyNotifylist_getseters[] = { + {"mask", (getter)PyNotifylist_mask_get, NULL, + PyNotifylist_mask_doc, NULL}, + {"away_check", (getter)PyNotifylist_away_check_get, NULL, + PyNotifylist_away_check_doc, NULL}, + {"idle_check_time", (getter)PyNotifylist_idle_check_time_get, NULL, + PyNotifylist_idle_check_time_doc, NULL}, + {NULL} +}; + +/* Methods */ +PyDoc_STRVAR(PyNotifylist_ircnets_doc, + "ircnets() -> list of str\n" + "\n" + "Return list of ircnets the notify is checked\n" +); +static PyObject *PyNotifylist_ircnets(PyNotifylist *self, PyObject *args) +{ + PyObject *list; + char **nets; + + RET_NULL_IF_INVALID(self->data); + + nets = NOTIFYLIST(self->data)->ircnets; + list = PyList_New(0); + if (!list) + return NULL; + + while (nets && *nets) + { + int ret; + PyObject *str = PyString_FromString(*nets); + + if (!str) + { + Py_DECREF(list); + return NULL; + } + + ret = PyList_Append(list, str); + Py_DECREF(str); + if (ret != 0) + { + Py_DECREF(list); + return NULL; + } + + nets++; + } + + return list; +} + +PyDoc_STRVAR(PyNotifylist_ircnets_match_doc, + "ircnets_match(ircnet) -> bool\n" + "\n" + "Return True if notify is checked in ircnet\n" +); +static PyObject *PyNotifylist_ircnets_match(PyNotifylist *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"ircnet", NULL}; + char *ircnet = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &ircnet)) + return NULL; + + return PyBool_FromLong(notifylist_ircnets_match(self->data, ircnet)); +} + +/* Methods for object */ +static PyMethodDef PyNotifylist_methods[] = { + {"ircnets", (PyCFunction)PyNotifylist_ircnets, METH_NOARGS, + PyNotifylist_ircnets_doc}, + {"ircnets_match", (PyCFunction)PyNotifylist_ircnets_match, METH_VARARGS | METH_KEYWORDS, + PyNotifylist_ircnets_match_doc}, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyNotifylistType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Notifylist", /*tp_name*/ + sizeof(PyNotifylist), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyNotifylist_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyNotifylist objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyNotifylist_methods, /* tp_methods */ + 0, /* tp_members */ + PyNotifylist_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyNotifylist_new, /* tp_new */ +}; + + +/* window item wrapper factory function */ +PyObject *pynotifylist_new(void *notifylist) +{ + PyNotifylist *pynotifylist; + + pynotifylist = py_inst(PyNotifylist, PyNotifylistType); + if (!pynotifylist) + return NULL; + + pynotifylist->data = notifylist; + pynotifylist->cleanup_installed = 1; + signal_add_last_data("notifylist remove", notifylist_cleanup, pynotifylist); + + return (PyObject *)pynotifylist; +} + +int notifylist_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyNotifylistType) < 0) + return 0; + + Py_INCREF(&PyNotifylistType); + PyModule_AddObject(py_module, "Notifylist", (PyObject *)&PyNotifylistType); + + return 1; +} diff --git a/src/objects/notifylist-object.h b/src/objects/notifylist-object.h new file mode 100644 index 0000000..9db6922 --- /dev/null +++ b/src/objects/notifylist-object.h @@ -0,0 +1,18 @@ +#ifndef _NOTIFYLIST_OBJECT_H_ +#define _NOTIFYLIST_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +typedef struct +{ + PyIrssiFinal_HEAD(void) +} PyNotifylist; + +extern PyTypeObject PyNotifylistType; + +int notifylist_object_init(void); +PyObject *pynotifylist_new(void *notifylist); +#define pynotifylist_check(op) PyObject_TypeCheck(op, &PyNotifylistType) + +#endif diff --git a/src/objects/process-object.c b/src/objects/process-object.c new file mode 100644 index 0000000..43c0fd7 --- /dev/null +++ b/src/objects/process-object.c @@ -0,0 +1,222 @@ +#include <Python.h> +#include "pyirssi_irc.h" +#include "pymodule.h" +#include "process-object.h" +#include "pycore.h" + +/* monitor "exec remove" signal */ +static void process_cleanup(PROCESS_REC *process, int status) +{ + PyProcess *pyprocess = signal_get_user_data(); + + if (process == pyprocess->data) + { + pyprocess->data = NULL; + pyprocess->cleanup_installed = 0; + signal_remove_data("exec remove", process_cleanup, pyprocess); + } +} + +static void PyProcess_dealloc(PyProcess *self) +{ + if (self->cleanup_installed) + signal_remove_data("exec remove", process_cleanup, self); + + Py_XDECREF(self->target_win); + + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyProcess_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyProcess *self; + + self = (PyProcess *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* Getters */ +PyDoc_STRVAR(PyProcess_id_doc, + "ID for the process" +); +static PyObject *PyProcess_id_get(PyProcess *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->id); +} + +PyDoc_STRVAR(PyProcess_name_doc, + "Name for the process (if given)" +); +static PyObject *PyProcess_name_get(PyProcess *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->name); +} + +PyDoc_STRVAR(PyProcess_args_doc, + "The command that is being executed" +); +static PyObject *PyProcess_args_get(PyProcess *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->args); +} + +PyDoc_STRVAR(PyProcess_pid_doc, + "PID for the executed command" +); +static PyObject *PyProcess_pid_get(PyProcess *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->pid); +} + +PyDoc_STRVAR(PyProcess_target_doc, + "send text with /msg <target> ..." +); +static PyObject *PyProcess_target_get(PyProcess *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->target); +} + +PyDoc_STRVAR(PyProcess_target_win_doc, + "print text to this window" +); +static PyObject *PyProcess_target_win_get(PyProcess *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_OBJ_OR_NONE(self->target_win); +} + +PyDoc_STRVAR(PyProcess_shell_doc, + "start the program via /bin/sh" +); +static PyObject *PyProcess_shell_get(PyProcess *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->shell); +} + +PyDoc_STRVAR(PyProcess_notice_doc, + "send text with /notice, not /msg if target is set" +); +static PyObject *PyProcess_notice_get(PyProcess *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->notice); +} + +PyDoc_STRVAR(PyProcess_silent_doc, + "don't print \"process exited with level xx\"" +); +static PyObject *PyProcess_silent_get(PyProcess *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->silent); +} + +/* specialized getters/setters */ +static PyGetSetDef PyProcess_getseters[] = { + {"id", (getter)PyProcess_id_get, NULL, + PyProcess_id_doc, NULL}, + {"name", (getter)PyProcess_name_get, NULL, + PyProcess_name_doc, NULL}, + {"args", (getter)PyProcess_args_get, NULL, + PyProcess_args_doc, NULL}, + {"pid", (getter)PyProcess_pid_get, NULL, + PyProcess_pid_doc, NULL}, + {"target", (getter)PyProcess_target_get, NULL, + PyProcess_target_doc, NULL}, + {"target_win", (getter)PyProcess_target_win_get, NULL, + PyProcess_target_win_doc, NULL}, + {"shell", (getter)PyProcess_shell_get, NULL, + PyProcess_shell_doc, NULL}, + {"notice", (getter)PyProcess_notice_get, NULL, + PyProcess_notice_doc, NULL}, + {"silent", (getter)PyProcess_silent_get, NULL, + PyProcess_silent_doc, NULL}, + {NULL} +}; + +/* Methods */ +/* Methods for object */ +static PyMethodDef PyProcess_methods[] = { + {NULL} /* Sentinel */ +}; + +PyTypeObject PyProcessType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Process", /*tp_name*/ + sizeof(PyProcess), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyProcess_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyProcess objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyProcess_methods, /* tp_methods */ + 0, /* tp_members */ + PyProcess_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyProcess_new, /* tp_new */ +}; + + +/* process factory function */ +PyObject *pyprocess_new(void *process) +{ + PyProcess *pyprocess; + + pyprocess = py_inst(PyProcess, PyProcessType); + if (!pyprocess) + return NULL; + + pyprocess->data = process; + pyprocess->cleanup_installed = 1; + signal_add_last_data("exec remove", process_cleanup, pyprocess); + + return (PyObject *)pyprocess; +} + +int process_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyProcessType) < 0) + return 0; + + Py_INCREF(&PyProcessType); + PyModule_AddObject(py_module, "Process", (PyObject *)&PyProcessType); + + return 1; +} diff --git a/src/objects/process-object.h b/src/objects/process-object.h new file mode 100644 index 0000000..041f187 --- /dev/null +++ b/src/objects/process-object.h @@ -0,0 +1,22 @@ +#ifndef _PROCESS_OBJECT_H_ +#define _PROCESS_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +/* forward */ +struct PROCESS_REC; + +typedef struct +{ + PyIrssiFinal_HEAD(struct PROCESS_REC) + PyObject *target_win; +} PyProcess; + +extern PyTypeObject PyProcessType; + +int process_object_init(void); +PyObject *pyprocess_new(void *process); +#define pyprocess_check(op) PyObject_TypeCheck(op, &PyProcessType) + +#endif diff --git a/src/objects/pyscript-object.c b/src/objects/pyscript-object.c new file mode 100644 index 0000000..ae4770e --- /dev/null +++ b/src/objects/pyscript-object.c @@ -0,0 +1,801 @@ +#include <Python.h> +#include <structmember.h> +#include "pyscript-object.h" +#include "pyirssi.h" +#include "pysignals.h" +#include "pymodule.h" +#include "pysource.h" +#include "pythemes.h" +#include "pystatusbar.h" + +/* handle cycles... + Can't think of any reason why the user would put script into one of the lists + but who knows. Call GC after unloading module. +*/ +static int PyScript_traverse(PyScript *self, visitproc visit, void *arg) +{ + Py_VISIT(self->module); + Py_VISIT(self->argv); + Py_VISIT(self->modules); + + return 0; +} + +static int PyScript_clear(PyScript *self) +{ + Py_CLEAR(self->module); + Py_CLEAR(self->argv); + Py_CLEAR(self->modules); + + return 0; +} + +static void PyScript_dealloc(PyScript* self) +{ + PyScript_clear(self); + pyscript_remove_signals((PyObject*)self); + pyscript_remove_sources((PyObject*)self); + + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyScript_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyScript *self; + PyObject *argv = NULL, *modules = NULL; + + argv = PyList_New(0); + if (!argv) + goto error; + + modules = PyDict_New(); + if (!modules) + goto error; + + self = (PyScript *)type->tp_alloc(type, 0); + if (!self) + 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, + "command_bind(command, func, catetory=None, priority=SIGNAL_PRIORITY_DEFAULT) -> None\n" + "\n" + "Add handler for a command\n" +); +static PyObject *PyScript_command_bind(PyScript *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"cmd", "func", "category", "priority", NULL}; + char *cmd; + PyObject *func; + char *category = NULL; + int priority = SIGNAL_PRIORITY_DEFAULT; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "sO|zi", kwlist, + &cmd, &func, &category, &priority)) + return NULL; + + if (!PyCallable_Check(func)) + return PyErr_Format(PyExc_TypeError, "func must be callable"); + + if (!pysignals_command_bind_list(&self->signals, cmd, func, category, priority)) + return PyErr_Format(PyExc_RuntimeError, "unable to bind command"); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyScript_signal_add_doc, + "signal_add(signal, func, priority=SIGNAL_PRIORITY_DEFAULT) -> None\n" + "\n" + "Add handler for signal" +); +static PyObject *PyScript_signal_add(PyScript *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"signal", "func", "priority", NULL}; + char *signal; + PyObject *func; + int priority = SIGNAL_PRIORITY_DEFAULT; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "sO|i", kwlist, + &signal, &func, &priority)) + return NULL; + + if (!PyCallable_Check(func)) + return PyErr_Format(PyExc_TypeError, "func must be callable"); + + if (!pysignals_signal_add_list(&self->signals, signal, func, priority)) + return PyErr_Format(PyExc_KeyError, "unable to find signal, '%s'", signal); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyScript_signal_remove_doc, + "signal_remove(signal, func=None) -> None\n" + "\n" + "Remove signal handler\n" +); +static PyObject *PyScript_signal_remove(PyScript *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"signal", "func", NULL}; + char *signal = ""; + PyObject *func = Py_None; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|O", kwlist, + &signal, &func)) + return NULL; + + if (!PyCallable_Check(func) && func != Py_None) + return PyErr_Format(PyExc_TypeError, "func must be callable or None"); + + if (func == Py_None) + func = NULL; + + if (!pysignals_remove_search(&self->signals, signal, func, PSG_SIGNAL)) + return PyErr_Format(PyExc_KeyError, "can't find signal"); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyScript_command_unbind_doc, + "command_unbind(command, func=None) -> None\n" + "\n" + "Remove command handler\n" +); +static PyObject *PyScript_command_unbind(PyScript *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"command", "func", NULL}; + char *command = ""; + PyObject *func = Py_None; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|O", kwlist, + &command, &func)) + return NULL; + + if (!PyCallable_Check(func) && func != Py_None) + return PyErr_Format(PyExc_TypeError, "func must be callable or None"); + + if (func == Py_None) + func = NULL; + + if (!pysignals_remove_search(&self->signals, command, func, PSG_COMMAND)) + return PyErr_Format(PyExc_KeyError, "can't find command"); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyScript_signal_register_doc, + "signal_register(signal, arglist) -> None\n" + "\n" + "Register a new dynamic signal for use with irssi_python\n" + "arglist is a string of character codes representing the type of each argument\n" + "of the signal handler function.\n" + "\n" + " Scalars\n" + " s -> char *\n" + " i -> int\n" + "\n" + " Chat objects\n" + " c -> CHATNET_REC\n" + " S -> SERVER_REC\n" + " C -> CHANNEL_REC\n" + " q -> QUERY_REC\n" + " n -> NICK_REC\n" + " W -> WI_ITEM_REC\n" + "\n" + " Irssi objects\n" + " d -> DCC_REC\n" + "\n" + " Other objects\n" + " r -> RECONNECT_REC\n" + " o -> COMMAND_REC\n" + " l -> LOG_REC\n" + " a -> RAWLOG_REC\n" + " g -> IGNORE_REC\n" + " b -> BAN_REC\n" + " N -> NETSPLIT_REC\n" + " e -> NETSPLIT_SERVER_REC\n" + " O -> NOTIFYLIST_REC\n" + " p -> PROCESS_REC\n" + " t -> TEXT_DEST_REC\n" + " w -> WINDOW_REC\n" +); +static PyObject *PyScript_signal_register(PyScript *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"signal", "arglist", NULL}; + static const char *good_codes = "sicSCqnWdrolagbNeOptw"; + char *signal = ""; + char *arglist = ""; + int i; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist, + &signal, &arglist)) + return NULL; + + for (i = 0; arglist[i]; i++) + if (!strchr(good_codes, arglist[i])) + return PyErr_Format(PyExc_TypeError, "invalid code, %c", arglist[i]); + + if (i >= SIGNAL_MAX_ARGUMENTS) + return PyErr_Format(PyExc_TypeError, + "arglist greater than SIGNAL_MAX_ARGUMENTS (%d)", + SIGNAL_MAX_ARGUMENTS); + + if (!pysignals_register(signal, arglist)) + return PyErr_Format(PyExc_TypeError, "signal present with different args"); + + self->registered_signals = g_slist_append(self->registered_signals, + g_strdup(signal)); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyScript_signal_unregister_doc, + "signal_unregister(signal) -> None\n" + "\n" + "Unregister dynamic signal\n" +); +static PyObject *PyScript_signal_unregister(PyScript *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"signal", NULL}; + char *signal = ""; + GSList *search; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &signal)) + return NULL; + + search = g_slist_find_custom(self->registered_signals, signal, (GCompareFunc)strcmp); + if (!search) + return PyErr_Format(PyExc_KeyError, "script has not registered that signal"); + + g_free(search->data); + self->registered_signals = g_slist_delete_link(self->registered_signals, search); + + if (!pysignals_unregister(signal)) + return PyErr_Format(PyExc_SystemError, + "script registered signal, but signal does not exist"); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyScript_timeout_add_doc, + "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", NULL}; + int msecs = 0; + PyObject *func = NULL; + PyObject *data = NULL; + int ret; + + 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"); + + 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_io_add_watch_doc, + "io_add_watch(fd, func, data=None, condition=IO_IN|IO_PRI) -> int source tag\n" +); +static PyObject *PyScript_io_add_watch(PyScript *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"fd", "func", "data", "condition", NULL}; + int fd = 0; + PyObject *pyfd = NULL; + PyObject *func = NULL; + PyObject *data = NULL; + int condition = G_IO_IN | G_IO_PRI; + int ret; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|Oi", kwlist, + &pyfd, &func, &data, &condition)) + return NULL; + + 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) -> bool\n" + "\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) +{ + static char *kwlist[] = {"tag", NULL}; + int tag = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, + &tag)) + return NULL; + + /* 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, + "settings_add_str(section, key, def) -> None\n" +); +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, + "settings_add_int(section, key, def) -> None\n" +); +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, + "settings_add_bool(section, key, def) -> None\n" +); +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, + "settings_add_time(section, key, def) -> None\n" +); +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, + "settings_add_level(section, key, def) -> None\n" +); +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, + "settings_add_size(section, key, def) -> None\n" +); +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; +} + +PyDoc_STRVAR(PyScript_statusbar_item_register_doc, + "statusbar_item_register(name, value=None, func=None) -> None\n" +); +static PyObject *PyScript_statusbar_item_register(PyScript *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"name", "value", "func", NULL}; + char *name = ""; + char *value = NULL; + PyObject *func = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|zO", kwlist, + &name, &value, &func)) + return NULL; + + pystatusbar_item_register((PyObject *)self, name, value, func); + + Py_RETURN_NONE; +} + +/* Methods for object */ +static PyMethodDef PyScript_methods[] = { + {"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}, + {"signal_remove", (PyCFunction)PyScript_signal_remove, METH_VARARGS | METH_KEYWORDS, + PyScript_signal_remove_doc}, + {"command_unbind", (PyCFunction)PyScript_command_unbind, METH_VARARGS | METH_KEYWORDS, + PyScript_command_unbind_doc}, + {"signal_register", (PyCFunction)PyScript_signal_register, METH_VARARGS | METH_KEYWORDS, + PyScript_signal_register_doc}, + {"signal_unregister", (PyCFunction)PyScript_signal_unregister, METH_VARARGS | METH_KEYWORDS, + PyScript_signal_unregister_doc}, + {"timeout_add", (PyCFunction)PyScript_timeout_add, METH_VARARGS | METH_KEYWORDS, + PyScript_timeout_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}, + {"statusbar_item_register", (PyCFunction)PyScript_statusbar_item_register, METH_VARARGS | METH_KEYWORDS, + PyScript_statusbar_item_register_doc}, + {NULL} /* Sentinel */ +}; + +static PyMemberDef PyScript_members[] = { + {"argv", T_OBJECT, offsetof(PyScript, argv), 0, "Script arguments"}, + {"module", T_OBJECT_EX, offsetof(PyScript, module), RO, "Script module"}, + {"modules", T_OBJECT_EX, offsetof(PyScript, modules), 0, "Imported modules"}, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyScriptType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Script", /*tp_name*/ + sizeof(PyScript), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyScript_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /*tp_flags*/ + "PyScript objects", /* tp_doc */ + (traverseproc)PyScript_traverse, /* tp_traverse */ + (inquiry)PyScript_clear, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyScript_methods, /* tp_methods */ + PyScript_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyScript_new, /* tp_new */ +}; + +/* PyScript factory function */ +PyObject *pyscript_new(PyObject *module, char **argv) +{ + PyObject *script; + + script = PyObject_CallFunction((PyObject*)&PyScriptType, "()"); + + if (script) + { + PyScript *scr = (PyScript *)script; + + while (*argv) + { + if (**argv != '\0') + { + 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); + } + + *argv++; + } + + Py_INCREF(module); + scr->module = module; + } + + return script; +} + +void pyscript_remove_signals(PyObject *script) +{ + GSList *node; + PyScript *self; + + g_return_if_fail(pyscript_check(script)); + + self = (PyScript *) script; + + /* remove bound signals */ + pysignals_remove_list(self->signals); + g_slist_free(self->signals); + self->signals = NULL; + + /* remove registered signals */ + for (node = self->registered_signals; node; node = node->next) + { + pysignals_unregister(node->data); + g_free(node->data); + } + + g_slist_free(self->registered_signals); + self->registered_signals = NULL; +} + +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; + + 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_remove_statusbars(PyObject *script) +{ + g_return_if_fail(pyscript_check(script)); + + pystatusbar_cleanup_script(script); +} + +void pyscript_clear_modules(PyObject *script) +{ + PyScript *self; + + g_return_if_fail(pyscript_check(script)); + + self = (PyScript *) 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_remove_statusbars(script); + pyscript_clear_modules(script); +} + +int pyscript_init(void) +{ + if (PyType_Ready(&PyScriptType) < 0) + return 0; + + Py_INCREF(&PyScriptType); + PyModule_AddObject(py_module, "Script", (PyObject *)&PyScriptType); + + return 1; +} + diff --git a/src/objects/pyscript-object.h b/src/objects/pyscript-object.h new file mode 100644 index 0000000..624a578 --- /dev/null +++ b/src/objects/pyscript-object.h @@ -0,0 +1,33 @@ +#ifndef _PYSCRIPT_OBJECT_H_ +#define _PYSCRIPT_OBJECT_H_ +#include <Python.h> +#include <glib.h> + +typedef struct { + PyObject_HEAD + PyObject *module; /* module object */ + PyObject *argv; /* list of argument strings from the load command */ + PyObject *modules; /* dict of imported modules for script */ + 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; + +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_remove_statusbars(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) +#define pyscript_get_module(scr) (((PyScript*)scr)->module) + +#endif diff --git a/src/objects/query-object.c b/src/objects/query-object.c new file mode 100644 index 0000000..47badd5 --- /dev/null +++ b/src/objects/query-object.c @@ -0,0 +1,172 @@ +#include <Python.h> +#include "pyirssi.h" +#include "pymodule.h" +#include "base-objects.h" +#include "window-item-object.h" +#include "query-object.h" +#include "server-object.h" +#include "pycore.h" + +/* monitor "query destroyed" signal */ +static void query_cleanup(QUERY_REC *query) +{ + PyQuery *pyquery = signal_get_user_data(); + + if (query == pyquery->data) + { + pyquery->data = NULL; + pyquery->cleanup_installed = 0; + signal_remove_data("query destroyed", query_cleanup, pyquery); + } +} + +static void PyQuery_dealloc(PyQuery *self) +{ + if (self->cleanup_installed) + signal_remove_data("query destroyed", query_cleanup, self); + + self->ob_type->tp_free((PyObject*)self); +} + +/* Getters */ +PyDoc_STRVAR(PyQuery_address_doc, + "Host address of the queries nick" +); +static PyObject *PyQuery_address_get(PyQuery *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->address); +} + +PyDoc_STRVAR(PyQuery_server_tag_doc, + "Server tag used for this nick (doesn't get erased if server gets disconnected)" +); +static PyObject *PyQuery_server_tag_get(PyQuery *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->server_tag); +} + +PyDoc_STRVAR(PyQuery_unwanted_doc, + "1 if the other side closed or some error occured (DCC chats)" +); +static PyObject *PyQuery_unwanted_get(PyQuery *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->unwanted); +} + +/* specialized getters/setters */ +static PyGetSetDef PyQuery_getseters[] = { + {"address", (getter)PyQuery_address_get, NULL, + PyQuery_address_doc, NULL}, + {"server_tag", (getter)PyQuery_server_tag_get, NULL, + PyQuery_server_tag_doc, NULL}, + {"unwanted", (getter)PyQuery_unwanted_get, NULL, + PyQuery_unwanted_doc, NULL}, + {NULL} +}; + +PyDoc_STRVAR(change_server_doc, + "change_server(server) -> None\n" + "\n" + "Change the active server for the query.\n" +); +static PyObject *PyQuery_change_server(PyQuery *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"server", NULL}; + PyObject *server; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &server)) + return NULL; + + if (!pyserver_check(server)) + return PyErr_Format(PyExc_TypeError, "argument must be server object"); + + query_change_server(self->data, ((PyServer*)server)->data); + + Py_RETURN_NONE; +} + +/* Methods for object */ +static PyMethodDef PyQuery_methods[] = { + {"change_server", (PyCFunction)PyQuery_change_server, METH_VARARGS | METH_KEYWORDS, + change_server_doc}, + + {NULL} /* Sentinel */ +}; + +PyTypeObject PyQueryType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Query", /*tp_name*/ + sizeof(PyQuery), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyQuery_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyQuery objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyQuery_methods, /* tp_methods */ + 0, /* tp_members */ + PyQuery_getseters, /* tp_getset */ + &PyWindowItemType, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + + +/* query factory function */ +PyObject *pyquery_new(void *query) +{ + static const char *BASE_NAME = "QUERY"; + PyObject *pyquery; + + pyquery = pywindow_item_sub_new(query, BASE_NAME, &PyQueryType); + if (pyquery) + { + PyQuery *pyq = (PyQuery *)pyquery; + signal_add_last_data("query destroyed", query_cleanup, pyq); + pyq->cleanup_installed = 1; + } + + return pyquery; +} + +int query_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyQueryType) < 0) + return 0; + + Py_INCREF(&PyQueryType); + PyModule_AddObject(py_module, "Query", (PyObject *)&PyQueryType); + + return 1; +} diff --git a/src/objects/query-object.h b/src/objects/query-object.h new file mode 100644 index 0000000..844ce55 --- /dev/null +++ b/src/objects/query-object.h @@ -0,0 +1,21 @@ +#ifndef _QUERY_OBJECT_H_ +#define _QUERY_OBJECT_H_ + +#include <Python.h> +#include "window-item-object.h" + +/* forward */ +struct _QUERY_REC; + +typedef struct +{ + PyWindowItem_HEAD(struct _QUERY_REC) +} PyQuery; + +extern PyTypeObject PyQueryType; + +int query_object_init(void); +PyObject *pyquery_new(void *query); +#define pyquery_check(op) PyObject_TypeCheck(op, &PyQueryType) + +#endif diff --git a/src/objects/rawlog-object.c b/src/objects/rawlog-object.c new file mode 100644 index 0000000..340d60a --- /dev/null +++ b/src/objects/rawlog-object.c @@ -0,0 +1,323 @@ +#include <Python.h> +#include "pyirssi_irc.h" +#include "pymodule.h" +#include "rawlog-object.h" +#include "pycore.h" + +/* monitor "????" signal */ +static void rawlog_cleanup(RAWLOG_REC *ban) +{ + /* XXX */ +} + +static void PyRawlog_dealloc(PyRawlog *self) +{ + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyRawlog_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyRawlog *self; + + self = (PyRawlog *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* XXX: Need function to create the rawlog */ + +/* Getters */ +PyDoc_STRVAR(PyRawlog_logging_doc, + "The raw log is being written to file currently." +); +static PyObject *PyRawlog_logging_get(PyRawlog *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->logging); +} + +PyDoc_STRVAR(PyRawlog_nlines_doc, + "Number of lines in rawlog." +); +static PyObject *PyRawlog_nlines_get(PyRawlog *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->nlines); +} + +/* specialized getters/setters */ +static PyGetSetDef PyRawlog_getseters[] = { + {"logging", (getter)PyRawlog_logging_get, NULL, + PyRawlog_logging_doc, NULL}, + {"nlines", (getter)PyRawlog_nlines_get, NULL, + PyRawlog_nlines_doc, NULL}, + {NULL} +}; + +/* Methods */ +PyDoc_STRVAR(PyRawlog_get_lines_doc, + "get_lines() -> list of str\n" + "\n" + "Return a list of lines for rawlog.\n" +); +static PyObject *PyRawlog_get_lines(PyRawlog *self, PyObject *args) +{ + PyObject *lines = NULL; + GSList *node; + + RET_NULL_IF_INVALID(self->data); + + lines = PyList_New(0); + if (!lines) + return NULL; + + for (node = self->data->lines; node; node = node->next) + { + int ret; + PyObject *line = PyString_FromString(node->data); + + if (!line) + { + Py_XDECREF(lines); + return NULL; + } + + ret = PyList_Append(lines, line); + Py_DECREF(line); + if (ret != 0) + { + Py_XDECREF(lines); + return NULL; + } + } + + return lines; +} + +PyDoc_STRVAR(PyRawlog_destroy_doc, + "destroy() -> None\n" + "\n" + "Destroy rawlog\n" +); +static PyObject *PyRawlog_destroy(PyRawlog *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + rawlog_destroy(self->data); + + /*XXX: what about signal handler ? */ + self->data = NULL; + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyRawlog_input_doc, + "input(str) -> None\n" + "\n" + "Send str to rawlog as input text.\n" +); +static PyObject *PyRawlog_input(PyRawlog *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; + + rawlog_input(self->data, str); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyRawlog_output_doc, + "output(str) -> None\n" + "\n" + "Send str to rawlog as output text.\n" +); +static PyObject *PyRawlog_output(PyRawlog *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; + + rawlog_output(self->data, str); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyRawlog_redirect_doc, + "redirect(str) -> None\n" + "\n" + "Send str to rawlog as redirection text." +); +static PyObject *PyRawlog_redirect(PyRawlog *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; + + rawlog_redirect(self->data, str); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyRawlog_open_doc, + "open(fname) -> None\n" + "\n" + "Start logging new messages in rawlog to specified file.\n" +); +static PyObject *PyRawlog_open(PyRawlog *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"fname", NULL}; + char *fname = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &fname)) + return NULL; + + rawlog_open(self->data, fname); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyRawlog_close_doc, + "close() -> None\n" + "\n" + "Stop logging to file\n" +); +static PyObject *PyRawlog_close(PyRawlog *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + rawlog_close(self->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyRawlog_save_doc, + "save(fname) -> None\n" + "\n" + "Save the current rawlog history to specified file.\n" +); +static PyObject *PyRawlog_save(PyRawlog *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"fname", NULL}; + char *fname = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &fname)) + return NULL; + + rawlog_save(self->data, fname); + + Py_RETURN_NONE; +} +/* Methods for object */ +static PyMethodDef PyRawlog_methods[] = { + {"get_lines", (PyCFunction)PyRawlog_get_lines, METH_NOARGS, + PyRawlog_get_lines_doc}, + {"destroy", (PyCFunction)PyRawlog_destroy, METH_NOARGS, + PyRawlog_destroy_doc}, + {"input", (PyCFunction)PyRawlog_input, METH_VARARGS | METH_KEYWORDS, + PyRawlog_input_doc}, + {"output", (PyCFunction)PyRawlog_output, METH_VARARGS | METH_KEYWORDS, + PyRawlog_output_doc}, + {"redirect", (PyCFunction)PyRawlog_redirect, METH_VARARGS | METH_KEYWORDS, + PyRawlog_redirect_doc}, + {"open", (PyCFunction)PyRawlog_open, METH_VARARGS | METH_KEYWORDS, + PyRawlog_open_doc}, + {"close", (PyCFunction)PyRawlog_close, METH_NOARGS, + PyRawlog_close_doc}, + {"save", (PyCFunction)PyRawlog_save, METH_VARARGS | METH_KEYWORDS, + PyRawlog_save_doc}, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyRawlogType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Rawlog", /*tp_name*/ + sizeof(PyRawlog), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyRawlog_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyRawlog objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyRawlog_methods, /* tp_methods */ + 0, /* tp_members */ + PyRawlog_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyRawlog_new, /* tp_new */ +}; + + +/* window item wrapper factory function */ +PyObject *pyrawlog_new(void *rlog) +{ + PyRawlog *pyrlog; + + pyrlog = py_inst(PyRawlog, PyRawlogType); + if (!pyrlog) + return NULL; + + pyrlog->data = rlog; + + return (PyObject *)pyrlog; +} + +int rawlog_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyRawlogType) < 0) + return 0; + + Py_INCREF(&PyRawlogType); + PyModule_AddObject(py_module, "Rawlog", (PyObject *)&PyRawlogType); + + return 1; +} diff --git a/src/objects/rawlog-object.h b/src/objects/rawlog-object.h new file mode 100644 index 0000000..d2a9a2d --- /dev/null +++ b/src/objects/rawlog-object.h @@ -0,0 +1,22 @@ +#ifndef _RAWLOG_OBJECT_H_ +#define _RAWLOG_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +/* forward */ +struct _RAWLOG_REC; + +typedef struct +{ + PyIrssiFinal_HEAD(struct _RAWLOG_REC) + int owned; +} PyRawlog; + +extern PyTypeObject PyRawlogType; + +int rawlog_object_init(void); +PyObject *pyrawlog_new(void *rlog); +#define pyrawlog_check(op) PyObject_TypeCheck(op, &PyRawlogType) + +#endif diff --git a/src/objects/reconnect-object.c b/src/objects/reconnect-object.c new file mode 100644 index 0000000..6483242 --- /dev/null +++ b/src/objects/reconnect-object.c @@ -0,0 +1,151 @@ +#include <Python.h> +#include "pyirssi.h" +#include "pymodule.h" +#include "pycore.h" +#include "factory.h" +#include "reconnect-object.h" + +/*XXX: no Reconnect cleanup/destroy sig. Maybe value copy the two members? */ + +static void PyReconnect_dealloc(PyReconnect *self) +{ + Py_XDECREF(self->connect); + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyReconnect_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyReconnect *self; + + self = (PyReconnect *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* Getters */ +PyDoc_STRVAR(PyReconnect_tag_doc, + "Unique numeric tag" +); +static PyObject *PyReconnect_tag_get(PyReconnect *self, void *closure) +{ + RECONNECT_REC *data = self->data; + RET_NULL_IF_INVALID(self->data); + + return PyInt_FromLong(data->tag); +} + +PyDoc_STRVAR(PyReconnect_next_connect_doc, + "Unix time stamp when the next connection occurs" +); +static PyObject *PyReconnect_next_connect_get(PyReconnect *self, void *closure) +{ + RECONNECT_REC *data = self->data; + RET_NULL_IF_INVALID(self->data); + + return PyLong_FromUnsignedLong(data->next_connect); +} + +PyDoc_STRVAR(PyReconnect_connect_doc, + "Connection object" +); +static PyObject *PyReconnect_connect_get(PyReconnect *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_OBJ_OR_NONE(self->connect); +} + +/* specialized getters/setters */ +static PyGetSetDef PyReconnect_getseters[] = { + {"tag", (getter)PyReconnect_tag_get, NULL, + PyReconnect_tag_doc, NULL}, + {"next_connect", (getter)PyReconnect_next_connect_get, NULL, + PyReconnect_next_connect_doc, NULL}, + {"connect", (getter)PyReconnect_connect_get, NULL, + PyReconnect_connect_doc, NULL}, + {NULL} +}; + +/* Methods for object */ +static PyMethodDef PyReconnect_methods[] = { + {NULL} /* Sentinel */ +}; + +PyTypeObject PyReconnectType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Reconnect", /*tp_name*/ + sizeof(PyReconnect), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyReconnect_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyReconnect objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyReconnect_methods, /* tp_methods */ + 0, /* tp_members */ + PyReconnect_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyReconnect_new, /* tp_new */ +}; + + +/* window item wrapper factory function */ +PyObject *pyreconnect_new(void *recon) +{ + RECONNECT_REC *rec = recon; + PyObject *connect; + PyReconnect *pyrecon; + + /* XXX: get a managed connect because there's no signals to manage reconnect */ + connect = py_irssi_chat_new(rec->conn, 1); + if (!connect) + return NULL; + + pyrecon = py_inst(PyReconnect, PyReconnectType); + if (!pyrecon) + return NULL; + + pyrecon->data = recon; + pyrecon->connect = connect; + + return (PyObject *)pyrecon; +} + +int reconnect_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyReconnectType) < 0) + return 0; + + Py_INCREF(&PyReconnectType); + PyModule_AddObject(py_module, "Reconnect", (PyObject *)&PyReconnectType); + + return 1; +} diff --git a/src/objects/reconnect-object.h b/src/objects/reconnect-object.h new file mode 100644 index 0000000..ec0b094 --- /dev/null +++ b/src/objects/reconnect-object.h @@ -0,0 +1,20 @@ +#ifndef _RECONNECT_OBJECT_H_ +#define _RECONNECT_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +/*XXX: no Reconnect cleanup/destroy sig. Maybe value copy the two members? */ +typedef struct +{ + PyIrssiFinal_HEAD(void) + PyObject *connect; +} PyReconnect; + +extern PyTypeObject PyReconnectType; + +int reconnect_object_init(void); +PyObject *pyreconnect_new(void *recon); +#define pyreconnect_check(op) PyObject_TypeCheck(op, &PyReconnectType) + +#endif diff --git a/src/objects/server-object.c b/src/objects/server-object.c new file mode 100644 index 0000000..31415b8 --- /dev/null +++ b/src/objects/server-object.c @@ -0,0 +1,807 @@ +#include <Python.h> +#include "pymodule.h" +#include "factory.h" +#include "pyirssi.h" +#include "pycore.h" +#include "pyutils.h" + +static void server_cleanup(SERVER_REC *server) +{ + PyServer *pyserver = signal_get_user_data(); + + if (server == pyserver->data) + { + if (pyserver->connect) + ((PyConnect *)pyserver->connect)->data = NULL; + + if (pyserver->rawlog) + ((PyRawlog *)pyserver->rawlog)->data = NULL; + + pyserver->data = NULL; + pyserver->cleanup_installed = 0; + signal_remove_data("server disconnected", server_cleanup, pyserver); + } +} + +static void PyServer_dealloc(PyServer *self) +{ + if (self->cleanup_installed) + signal_remove_data("server disconnected", server_cleanup, self); + + Py_XDECREF(self->connect); + Py_XDECREF(self->rawlog); + + self->ob_type->tp_free((PyObject*)self); +} + +/* Getters */ +PyDoc_STRVAR(PyServer_connect_time_doc, + "Time when connect() to server finished" +); +static PyObject *PyServer_connect_time_get(PyServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromLong(self->data->connect_time); +} + +PyDoc_STRVAR(PyServer_real_connect_time_doc, + "Time when server sent 'connected' message" +); +static PyObject *PyServer_real_connect_time_get(PyServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromLong(self->data->real_connect_time); +} + +PyDoc_STRVAR(PyServer_tag_doc, + "Unique server tag" +); +static PyObject *PyServer_tag_get(PyServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->tag); +} + +PyDoc_STRVAR(PyServer_nick_doc, + "Current nick" +); +static PyObject *PyServer_nick_get(PyServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->nick); +} + +PyDoc_STRVAR(PyServer_connected_doc, + "Is connection finished? 1|0" +); +static PyObject *PyServer_connected_get(PyServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->connected); +} + +PyDoc_STRVAR(PyServer_connection_lost_doc, + "Did we lose the connection (1) or was the connection just /DISCONNECTed (0)" +); +static PyObject *PyServer_connection_lost_get(PyServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->connection_lost); +} + +PyDoc_STRVAR(PyServer_rawlog_doc, + "Rawlog object for the server" +); +static PyObject *PyServer_rawlog_get(PyServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_OBJ_OR_NONE(self->rawlog); +} + +PyDoc_STRVAR(PyServer_connect_doc, + "Connect object for the server" +); +static PyObject *PyServer_connect_get(PyServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_OBJ_OR_NONE(self->connect); +} + +PyDoc_STRVAR(PyServer_version_doc, + "Server version" +); +static PyObject *PyServer_version_get(PyServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->version); +} + +PyDoc_STRVAR(PyServer_last_invite_doc, + "Last channel we were invited to" +); +static PyObject *PyServer_last_invite_get(PyServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->last_invite); +} + +PyDoc_STRVAR(PyServer_server_operator_doc, + "Are we server operator (IRC op) 1|0" +); +static PyObject *PyServer_server_operator_get(PyServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->server_operator); +} + +PyDoc_STRVAR(PyServer_usermode_away_doc, + "Are we marked as away? 1|0" +); +static PyObject *PyServer_usermode_away_get(PyServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->usermode_away); +} + +PyDoc_STRVAR(PyServer_away_reason_doc, + "Away reason message" +); +static PyObject *PyServer_away_reason_get(PyServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->away_reason); +} + +PyDoc_STRVAR(PyServer_banned_doc, + "Were we banned from this server? 1|0" +); +static PyObject *PyServer_banned_get(PyServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->banned); +} + +PyDoc_STRVAR(PyServer_lag_doc, + "Current lag to server in milliseconds" +); +static PyObject *PyServer_lag_get(PyServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->lag); +} + +static PyGetSetDef PyServer_getseters[] = { + {"connect_time", (getter)PyServer_connect_time_get, NULL, + PyServer_connect_time_doc, NULL}, + {"real_connect_time", (getter)PyServer_real_connect_time_get, NULL, + PyServer_real_connect_time_doc, NULL}, + {"tag", (getter)PyServer_tag_get, NULL, + PyServer_tag_doc, NULL}, + {"nick", (getter)PyServer_nick_get, NULL, + PyServer_nick_doc, NULL}, + {"connected", (getter)PyServer_connected_get, NULL, + PyServer_connected_doc, NULL}, + {"connection_lost", (getter)PyServer_connection_lost_get, NULL, + PyServer_connection_lost_doc, NULL}, + {"rawlog", (getter)PyServer_rawlog_get, NULL, + PyServer_rawlog_doc, NULL}, + {"connect", (getter)PyServer_connect_get, NULL, + PyServer_connect_doc, NULL}, + {"version", (getter)PyServer_version_get, NULL, + PyServer_version_doc, NULL}, + {"last_invite", (getter)PyServer_last_invite_get, NULL, + PyServer_last_invite_doc, NULL}, + {"server_operator", (getter)PyServer_server_operator_get, NULL, + PyServer_server_operator_doc, NULL}, + {"usermode_away", (getter)PyServer_usermode_away_get, NULL, + PyServer_usermode_away_doc, NULL}, + {"away_reason", (getter)PyServer_away_reason_get, NULL, + PyServer_away_reason_doc, NULL}, + {"banned", (getter)PyServer_banned_get, NULL, + PyServer_banned_doc, NULL}, + {"lag", (getter)PyServer_lag_get, NULL, + PyServer_lag_doc, NULL}, + {NULL} +}; + +/* Methods */ +PyDoc_STRVAR(print_doc, + "prnt(channel, str, level) -> None\n" + "\n" + "Print to server\n" +); +static PyObject *PyServer_prnt(PyServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"channel", "str", "level", NULL}; + char *str, *channel; + int level = MSGLEVEL_CLIENTNOTICE; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss|i", kwlist, &channel, &str, &level)) + return NULL; + + printtext_string(self->data, channel, level, str); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(command_doc, + "command(cmd) -> None\n" + "\n" + "Send command\n" +); +static PyObject *PyServer_command(PyServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"cmd", NULL}; + char *cmd; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &cmd)) + return NULL; + + py_command(cmd, self->data, NULL); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(disconnect_doc, + "disconnect() -> None\n" + "\n" + "Disconnect from server\n" +); +static PyObject *PyServer_disconnect(PyServer *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + server_disconnect(self->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(isnickflag_doc, + "isnickflag(flag) -> bool\n" + "\n" + "Returns True if flag is a nick mode flag (@, + or % in IRC)\n" +); +static PyObject *PyServer_isnickflag(PyServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"flag", NULL}; + char flag; + int ret; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "c", kwlist, &flag)) + return NULL; + + ret = self->data->isnickflag(self->data, flag); + + return PyBool_FromLong(ret); +} + +PyDoc_STRVAR(ischannel_doc, + "ischannel(data) -> bool\n" + "\n" + "Returns True if start of `data' seems to mean channel.\n" +); +static PyObject *PyServer_ischannel(PyServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"data", NULL}; + char *data; + int ret; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &data)) + return NULL; + + ret = self->data->ischannel(self->data, data); + + return PyBool_FromLong(ret); +} + +PyDoc_STRVAR(get_nick_flags_doc, + "get_nick_flags() -> str\n" + "\n" + "Returns nick flag characters in order: op, voice, halfop (\"@+%\") in IRC\n" +); +static PyObject *PyServer_get_nick_flags(PyServer *self, PyObject *args) +{ + char *ret; + + RET_NULL_IF_INVALID(self->data); + + ret = (char *)self->data->get_nick_flags(self->data); + + return PyString_FromString(ret); +} + +PyDoc_STRVAR(send_message_doc, + "send_message(target, msg, target_type) -> None\n" + "\n" + "Sends a message to nick/channel. target_type 0 = channel, 1 = nick\n" +); +static PyObject *PyServer_send_message(PyServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"target", "msg", "target_type", NULL}; + char *target, *msg; + int target_type; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ssi", kwlist, &target, &msg, &target_type)) + return NULL; + + self->data->send_message(self->data, target, msg, target_type); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(channels_join_doc, + "channels_join(channels, automatic=False) -> None\n" + "\n" + "Join to channels in server. `channels' may also contain keys for\n" + "channels just like with /JOIN command. `automatic' specifies if this\n" + "channel was joined 'automatically' or if it was joined because join\n" + "was requested by user. If channel join is 'automatic', irssi doesn't\n" + "jump to the window where the channel was joined.\n" +); +static PyObject *PyServer_channels_join(PyServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"channels", "automatic", NULL}; + char *channels; + int automatic = 0; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, &channels, &automatic)) + return NULL; + + self->data->channels_join(self->data, channels, automatic); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyServer_window_item_find_doc, + "window_item_find(name) -> WindowItem object or None\n" + "\n" + "Find window item that matches best to given arguments\n" +); +static PyObject *PyServer_window_item_find(PyServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"name", NULL}; + char *name = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &name)) + return NULL; + + return py_irssi_chat_new(window_item_find(self->data, name), 1); +} + +PyDoc_STRVAR(PyServer_window_find_item_doc, + "window_find_item(name) -> Window object or None\n" + "\n" + "Find window which contains window item with specified name/server\n" +); +static PyObject *PyServer_window_find_item(PyServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"name", NULL}; + char *name = ""; + WINDOW_REC *win; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &name)) + return NULL; + + win = window_find_item(self->data, name); + if (win) + return pywindow_new(win); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyServer_window_find_level_doc, + "window_find_level(level) -> Window object or None\n" + "\n" + "Find window with level\n" +); +static PyObject *PyServer_window_find_level(PyServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"level", NULL}; + int level = 0; + WINDOW_REC *win; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, + &level)) + return NULL; + + win = window_find_level(self->data, level); + if (win) + return pywindow_new(win); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyServer_window_find_closest_doc, + "window_find_closest(name, level) -> Window object or None\n" + "\n" + "Find window that matches best to given arguments. `name' can be either\n" + "window name or name of one of the window items.\n" +); +static PyObject *PyServer_window_find_closest(PyServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"name", "level", NULL}; + char *name = ""; + int level = 0; + WINDOW_REC *win; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "si", kwlist, + &name, &level)) + return NULL; + + win = window_find_closest(self->data, name, level); + if (win) + return pywindow_new(win); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyServer_channels_doc, + "channels() -> list of Channel objects\n" + "\n" + "Return list of channels for server\n" +); +static PyObject *PyServer_channels(PyServer *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + return py_irssi_chatlist_new(self->data->channels, 1); +} + +PyDoc_STRVAR(PyServer_channel_find_doc, + "channel_find(name) -> Channel object or None\n" + "\n" + "Find channel from this server\n" +); +static PyObject *PyServer_channel_find(PyServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"name", NULL}; + char *name = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &name)) + return NULL; + + return py_irssi_chat_new(channel_find(self->data, name), 1); +} + +PyDoc_STRVAR(PyServer_nicks_get_same_doc, + "nicks_get_same(nick)\n" + "\n" + "Return all nick objects in all channels in server. List is in format:\n" + "[(Channel, Nick), (Channel, Nick), ...]\n" +); +static PyObject *PyServer_nicks_get_same(PyServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"nick", NULL}; + char *nick = ""; + GSList *list, *node; + PyObject *pylist = NULL; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &nick)) + return NULL; + + pylist = PyList_New(0); + if (!pylist) + return NULL; + + list = nicklist_get_same(self->data, nick); + for (node = list; node != NULL; node = node->next->next) + { + int ret; + PyObject *tup; + + tup = Py_BuildValue("(NN)", + py_irssi_chat_new(node->data, 1), + py_irssi_chat_new(node->next->data, 1)); + if (!tup) + { + Py_XDECREF(pylist); + return NULL; + } + + ret = PyList_Append(pylist, tup); + Py_DECREF(tup); + if (ret != 0) + { + Py_XDECREF(pylist); + return NULL; + } + } + + return pylist; +} + +PyDoc_STRVAR(PyServer_queries_doc, + "queries() -> list of Query objects\n" + "\n" + "Return a list of queries for server.\n" +); +static PyObject *PyServer_queries(PyServer *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + return py_irssi_chatlist_new(self->data->queries, 1); +} + +PyDoc_STRVAR(PyServer_query_find_doc, + "query_find(nick) -> Query object or None\n" + "\n" + "Find a query on this server.\n" +); +static PyObject *PyServer_query_find(PyServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"nick", NULL}; + char *nick = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &nick)) + return NULL; + + return py_irssi_chat_new(query_find(self->data, nick), 1); +} + +PyDoc_STRVAR(PyServer_mask_match_doc, + "mask_match(mask, nick, user, host) -> bool\n" + "\n" + "Return true if mask matches nick!user@host\n" +); +static PyObject *PyServer_mask_match(PyServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"mask", "nick", "user", "host", NULL}; + char *mask = ""; + char *nick = ""; + char *user = ""; + char *host = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ssss", kwlist, + &mask, &nick, &user, &host)) + return NULL; + + return PyBool_FromLong(mask_match(self->data, mask, nick, user, host)); +} + +PyDoc_STRVAR(PyServer_mask_match_address_doc, + "mask_match_address(mask, nick, address) -> bool\n" + "\n" + "Return True if mask matches nick!address\n" +); +static PyObject *PyServer_mask_match_address(PyServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"mask", "nick", "address", NULL}; + char *mask = ""; + char *nick = ""; + char *address = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "sss", kwlist, + &mask, &nick, &address)) + return NULL; + + return PyBool_FromLong(mask_match_address(self->data, mask, nick, address)); +} + +PyDoc_STRVAR(PyServer_masks_match_doc, + "masks_match(masks, nick, address) -> bool\n" + "\n" + "Return True if any mask in the masks (string separated by spaces)\n" + "matches nick!address\n" +); +static PyObject *PyServer_masks_match(PyServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"masks", "nick", "address", NULL}; + char *masks = ""; + char *nick = ""; + char *address = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "sss", kwlist, + &masks, &nick, &address)) + return NULL; + + return PyBool_FromLong(masks_match(self->data, masks, nick, address)); +} + +PyDoc_STRVAR(PyServer_ignore_check_doc, + "ignore_check(nick, host, channel, text, level) -> bool\n" + "\n" + "Return True if ignore matches\n" +); +static PyObject *PyServer_ignore_check(PyServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"nick", "host", "channel", "text", "level", NULL}; + char *nick = ""; + char *host = ""; + char *channel = ""; + char *text = ""; + int level = 0; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ssssi", kwlist, + &nick, &host, &channel, &text, &level)) + return NULL; + + return PyBool_FromLong(ignore_check(self->data, + nick, host, channel, text, level)); +} + +/* Methods for object */ +static PyMethodDef PyServer_methods[] = { + {"prnt", (PyCFunction)PyServer_prnt, METH_VARARGS | METH_KEYWORDS, + print_doc}, + {"command", (PyCFunction)PyServer_command, METH_VARARGS | METH_KEYWORDS, + command_doc}, + {"disconnect", (PyCFunction)PyServer_disconnect, METH_NOARGS, + disconnect_doc}, + {"isnickflag", (PyCFunction)PyServer_isnickflag, METH_VARARGS | METH_KEYWORDS, + isnickflag_doc}, + {"ischannel", (PyCFunction)PyServer_ischannel, METH_VARARGS | METH_KEYWORDS, + ischannel_doc}, + {"get_nick_flags", (PyCFunction)PyServer_get_nick_flags, METH_NOARGS, + get_nick_flags_doc}, + {"send_message", (PyCFunction)PyServer_send_message, METH_VARARGS | METH_KEYWORDS, + send_message_doc}, + {"channels_join", (PyCFunction)PyServer_channels_join, METH_VARARGS | METH_KEYWORDS, + channels_join_doc}, + {"window_item_find", (PyCFunction)PyServer_window_item_find, METH_VARARGS | METH_KEYWORDS, + PyServer_window_item_find_doc}, + {"window_find_item", (PyCFunction)PyServer_window_find_item, METH_VARARGS | METH_KEYWORDS, + PyServer_window_find_item_doc}, + {"window_find_level", (PyCFunction)PyServer_window_find_level, METH_VARARGS | METH_KEYWORDS, + PyServer_window_find_level_doc}, + {"window_find_closest", (PyCFunction)PyServer_window_find_closest, METH_VARARGS | METH_KEYWORDS, + PyServer_window_find_closest_doc}, + {"channels", (PyCFunction)PyServer_channels, METH_NOARGS, + PyServer_channels_doc}, + {"channel_find", (PyCFunction)PyServer_channel_find, METH_VARARGS | METH_KEYWORDS, + PyServer_channel_find_doc}, + {"nicks_get_same", (PyCFunction)PyServer_nicks_get_same, METH_VARARGS | METH_KEYWORDS, + PyServer_nicks_get_same_doc}, + {"queries", (PyCFunction)PyServer_queries, METH_NOARGS, + PyServer_queries_doc}, + {"query_find", (PyCFunction)PyServer_query_find, METH_VARARGS | METH_KEYWORDS, + PyServer_query_find_doc}, + {"mask_match", (PyCFunction)PyServer_mask_match, METH_VARARGS | METH_KEYWORDS, + PyServer_mask_match_doc}, + {"mask_match_address", (PyCFunction)PyServer_mask_match_address, METH_VARARGS | METH_KEYWORDS, + PyServer_mask_match_address_doc}, + {"masks_match", (PyCFunction)PyServer_masks_match, METH_VARARGS | METH_KEYWORDS, + PyServer_masks_match_doc}, + {"ignore_check", (PyCFunction)PyServer_ignore_check, METH_VARARGS | METH_KEYWORDS, + PyServer_ignore_check_doc}, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyServerType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Server", /*tp_name*/ + sizeof(PyServer), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyServer_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyServer objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyServer_methods, /* tp_methods */ + 0, /* tp_members */ + PyServer_getseters, /* tp_getset */ + &PyIrssiChatBaseType, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + + +/* server factory function + connect arg should point to a wrapped SERVER_CONNECT */ +PyObject *pyserver_sub_new(void *server, PyTypeObject *subclass) +{ + static const char *SERVER_TYPE = "SERVER"; + SERVER_REC *srec = server; + PyServer *pyserver = NULL; + PyObject *rawlog = NULL; + PyObject *connect = NULL; + + g_return_val_if_fail(server != NULL, NULL); + + connect = py_irssi_chat_new(srec->connrec, 0); + if (!connect) + return NULL; + + /* FIXME */ + /* + if (srec->rawlog) + { + rawlog = pyrawlog_new(srec->rawlog); + if (!rawlog) + return NULL; + } + */ + + pyserver = py_instp(PyServer, subclass); + if (!pyserver) + return NULL; + + pyserver->base_name = SERVER_TYPE; + pyserver->data = server; + signal_add_last_data("server disconnected", server_cleanup, pyserver); + pyserver->cleanup_installed = 1; + pyserver->rawlog = rawlog; + pyserver->connect = connect; + + return (PyObject *)pyserver; +} + +PyObject *pyserver_new(void *server) +{ + return pyserver_sub_new(server, &PyServerType); +} + +int server_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyServerType) < 0) + return 0; + + Py_INCREF(&PyServerType); + PyModule_AddObject(py_module, "Server", (PyObject *)&PyServerType); + + return 1; +} diff --git a/src/objects/server-object.h b/src/objects/server-object.h new file mode 100644 index 0000000..0703f47 --- /dev/null +++ b/src/objects/server-object.h @@ -0,0 +1,28 @@ +#ifndef _SERVER_OBJECT_H_ +#define _SERVER_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +/* forward */ +struct _SERVER_REC; + +#define PyServer_HEAD(type) \ + PyIrssi_HEAD(type) \ + PyObject *connect; \ + PyObject *rawlog; + +typedef struct +{ + PyServer_HEAD(struct _SERVER_REC) +} PyServer; + +extern PyTypeObject PyServerType; + +int server_object_init(void); +PyObject *pyserver_sub_new(void *server, PyTypeObject *subclass); +PyObject *pyserver_new(void *server); + +#define pyserver_check(op) PyObject_TypeCheck(op, &PyServerType) + +#endif diff --git a/src/objects/statusbar-item-object.c b/src/objects/statusbar-item-object.c new file mode 100644 index 0000000..f6768af --- /dev/null +++ b/src/objects/statusbar-item-object.c @@ -0,0 +1,253 @@ +#include <Python.h> +#include "pyirssi.h" +#include "pymodule.h" +#include "factory.h" +#include "statusbar-item-object.h" + +/* monitor "statusbar item destroyed" signal */ +static void statusbar_item_cleanup(SBAR_ITEM_REC *sbar_item) +{ + PyStatusbarItem *pysbar_item = signal_get_user_data(); + + if (sbar_item == pysbar_item->data) + { + pysbar_item->data = NULL; + pysbar_item->cleanup_installed = 0; + signal_remove_data("statusbar item_destroy", statusbar_item_cleanup, pysbar_item); + } +} + +static void PyStatusbarItem_dealloc(PyStatusbarItem *self) +{ + if (self->cleanup_installed) + signal_remove_data("sbar_itemlist remove", statusbar_item_cleanup, self); + + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyStatusbarItem_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyStatusbarItem *self; + + self = (PyStatusbarItem *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* Getter */ +PyDoc_STRVAR(PyStatusbarItem_min_size_doc, + "min size" +); +static PyObject *PyStatusbarItem_min_size_get(PyStatusbarItem *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->min_size); +} + +PyDoc_STRVAR(PyStatusbarItem_max_size_doc, + "max size" +); +static PyObject *PyStatusbarItem_max_size_get(PyStatusbarItem *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->max_size); +} + +PyDoc_STRVAR(PyStatusbarItem_xpos_doc, + "x position" +); +static PyObject *PyStatusbarItem_xpos_get(PyStatusbarItem *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->xpos); +} + +PyDoc_STRVAR(PyStatusbarItem_size_doc, + "size" +); +static PyObject *PyStatusbarItem_size_get(PyStatusbarItem *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->size); +} + +PyDoc_STRVAR(PyStatusbarItem_window_doc, + "parent window for statusbar item" +); +static PyObject *PyStatusbarItem_window_get(PyStatusbarItem *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_OBJ_OR_NONE(self->window); +} + +/* setters */ +static int py_setint(int *iv, PyObject *value) +{ + int tmp; + + if (value == NULL) + { + PyErr_SetString(PyExc_AttributeError, "can't delete member"); + return -1; + } + + if (!PyInt_Check(value)) + { + PyErr_SetString(PyExc_TypeError, "value must be int"); + return -1; + } + + tmp = PyInt_AsLong(value); + if (PyErr_Occurred()) + return -1; + + *iv = tmp; + + return 0; +} + +static int PyStatusbarItem_min_size_set(PyStatusbarItem *self, PyObject *value, void *closure) +{ + return py_setint(&self->data->min_size, value); +} + +static int PyStatusbarItem_max_size_set(PyStatusbarItem *self, PyObject *value, void *closure) +{ + return py_setint(&self->data->max_size, value); +} + +/* specialized getters/setters */ +static PyGetSetDef PyStatusbarItem_getseters[] = { + {"min_size", (getter)PyStatusbarItem_min_size_get, (setter)PyStatusbarItem_min_size_set, + PyStatusbarItem_min_size_doc, NULL}, + {"max_size", (getter)PyStatusbarItem_max_size_get, (setter)PyStatusbarItem_max_size_set, + PyStatusbarItem_max_size_doc, NULL}, + {"xpos", (getter)PyStatusbarItem_xpos_get, NULL, + PyStatusbarItem_xpos_doc, NULL}, + {"size", (getter)PyStatusbarItem_size_get, NULL, + PyStatusbarItem_size_doc, NULL}, + {"window", (getter)PyStatusbarItem_window_get, NULL, + PyStatusbarItem_window_doc, NULL}, + {NULL} +}; + +/* Methods */ +PyDoc_STRVAR(PyStatusbarItem_default_handler_doc, + "default_handler(get_size_only, str=None, data="", escape_vars=True) -> None\n" + "\n" + "Run default handler of item to print to statusbar\n" +); +static PyObject *PyStatusbarItem_default_handler(PyStatusbarItem *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"get_size_only", "str", "data", "escape_vars", NULL}; + int get_size_only = 0; + char *str = NULL; + char *data = ""; + int escape_vars = TRUE; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "i|zsi", kwlist, + &get_size_only, &str, &data, &escape_vars)) + return NULL; + + if (str && !*str) + str = NULL; + + statusbar_item_default_handler(self->data, get_size_only, str, data, escape_vars); + + Py_RETURN_NONE; +} + +/* Methods for object */ +static PyMethodDef PyStatusbarItem_methods[] = { + {"default_handler", (PyCFunction)PyStatusbarItem_default_handler, METH_VARARGS | METH_KEYWORDS, + PyStatusbarItem_default_handler_doc}, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyStatusbarItemType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "StatusbarItem", /*tp_name*/ + sizeof(PyStatusbarItem), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyStatusbarItem_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyStatusbarItem objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyStatusbarItem_methods, /* tp_methods */ + 0, /* tp_members */ + PyStatusbarItem_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyStatusbarItem_new, /* tp_new */ +}; + + +/* sbar_item factory function */ +PyObject *pystatusbar_item_new(void *sbar_item) +{ + SBAR_ITEM_REC *si; + PyStatusbarItem *pysbar_item; + PyObject *window = NULL; + + si = sbar_item; + if (si->bar->parent_window) + { + window = pywindow_new(si->bar->parent_window); + if (!window) + return NULL; + } + + pysbar_item = py_inst(PyStatusbarItem, PyStatusbarItemType); + if (!pysbar_item) + return NULL; + + pysbar_item->window = window; + + pysbar_item->data = sbar_item; + pysbar_item->cleanup_installed = 1; + signal_add_last_data("statusbar item destroyed", statusbar_item_cleanup, pysbar_item); + + return (PyObject *)pysbar_item; +} + +int statusbar_item_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyStatusbarItemType) < 0) + return 0; + + Py_INCREF(&PyStatusbarItemType); + PyModule_AddObject(py_module, "StatusbarItem", (PyObject *)&PyStatusbarItemType); + + return 1; +} diff --git a/src/objects/statusbar-item-object.h b/src/objects/statusbar-item-object.h new file mode 100644 index 0000000..19243f4 --- /dev/null +++ b/src/objects/statusbar-item-object.h @@ -0,0 +1,22 @@ +#ifndef _SBAR_ITEM_OBJECT_H_ +#define _SBAR_ITEM_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +/* forward */ +struct SBAR_ITEM_REC; + +typedef struct +{ + PyIrssiFinal_HEAD(struct SBAR_ITEM_REC) + PyObject *window; +} PyStatusbarItem; + +extern PyTypeObject PyStatusbarItemType; + +int statusbar_item_object_init(void); +PyObject *pystatusbar_item_new(void *sbar_item); +#define pystatusbar_item_check(op) PyObject_TypeCheck(op, &PyStatusbarItemType) + +#endif diff --git a/src/objects/textdest-object.c b/src/objects/textdest-object.c new file mode 100644 index 0000000..2ad6523 --- /dev/null +++ b/src/objects/textdest-object.c @@ -0,0 +1,285 @@ +#include <Python.h> +#include "pyirssi_irc.h" +#include "pymodule.h" +#include "textdest-object.h" +#include "factory.h" +#include "pycore.h" + +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); +} + +static PyObject *PyTextDest_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyTextDest *self; + + self = (PyTextDest *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + 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" +); +static PyObject *PyTextDest_window_get(PyTextDest *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_OBJ_OR_NONE(self->window); +} + +PyDoc_STRVAR(PyTextDest_server_doc, + "Target server" +); +static PyObject *PyTextDest_server_get(PyTextDest *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_OBJ_OR_NONE(self->server); +} + +PyDoc_STRVAR(PyTextDest_target_doc, + "Target channel/query/etc name" +); +static PyObject *PyTextDest_target_get(PyTextDest *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->target); +} + +PyDoc_STRVAR(PyTextDest_level_doc, + "Text level" +); +static PyObject *PyTextDest_level_get(PyTextDest *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->level); +} + +PyDoc_STRVAR(PyTextDest_hilight_priority_doc, + "Priority for the hilighted text" +); +static PyObject *PyTextDest_hilight_priority_get(PyTextDest *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->hilight_priority); +} + +PyDoc_STRVAR(PyTextDest_hilight_color_doc, + "Color for the hilighted text" +); +static PyObject *PyTextDest_hilight_color_get(PyTextDest *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->hilight_color); +} + +/* specialized getters/setters */ +static PyGetSetDef PyTextDest_getseters[] = { + {"window", (getter)PyTextDest_window_get, NULL, + PyTextDest_window_doc, NULL}, + {"server", (getter)PyTextDest_server_get, NULL, + PyTextDest_server_doc, NULL}, + {"target", (getter)PyTextDest_target_get, NULL, + PyTextDest_target_doc, NULL}, + {"level", (getter)PyTextDest_level_get, NULL, + PyTextDest_level_doc, NULL}, + {"hilight_priority", (getter)PyTextDest_hilight_priority_get, NULL, + PyTextDest_hilight_priority_doc, NULL}, + {"hilight_color", (getter)PyTextDest_hilight_color_get, NULL, + PyTextDest_hilight_color_doc, NULL}, + {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 */ +}; + +PyTypeObject PyTextDestType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "TextDest", /*tp_name*/ + sizeof(PyTextDest), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyTextDest_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + PyTextDest_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyTextDest_methods, /* tp_methods */ + 0, /* tp_members */ + PyTextDest_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)PyTextDest_init, /* tp_init */ + 0, /* tp_alloc */ + PyTextDest_new, /* tp_new */ +}; + +static int pytextdest_setup(PyTextDest *pytdest, void *td, int owned) +{ + PyObject *window, *server; + TEXT_DEST_REC *tdest = td; + + 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 0; + } + + 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; +} + +int textdest_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyTextDestType) < 0) + return 0; + + Py_INCREF(&PyTextDestType); + PyModule_AddObject(py_module, "TextDest", (PyObject *)&PyTextDestType); + + return 1; +} diff --git a/src/objects/textdest-object.h b/src/objects/textdest-object.h new file mode 100644 index 0000000..3e273e6 --- /dev/null +++ b/src/objects/textdest-object.h @@ -0,0 +1,24 @@ +#ifndef _TEXTDEST_OBJECT_H_ +#define _TEXTDEST_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +/* forward */ +struct _TEXT_DEST_REC; + +typedef struct +{ + PyIrssiFinal_HEAD(struct _TEXT_DEST_REC) + PyObject *window; + PyObject *server; + int owned; +} PyTextDest; + +extern PyTypeObject PyTextDestType; + +int textdest_object_init(void); +PyObject *pytextdest_new(void *td); +#define pytextdest_check(op) PyObject_TypeCheck(op, &PyTextDestType) + +#endif diff --git a/src/objects/theme-object.c b/src/objects/theme-object.c new file mode 100644 index 0000000..0f12217 --- /dev/null +++ b/src/objects/theme-object.c @@ -0,0 +1,195 @@ +#include <Python.h> +#include "pyirssi.h" +#include "pymodule.h" +#include "theme-object.h" +#include "factory.h" +#include "pycore.h" + +/* monitor "theme destroyed" signal */ +static void theme_cleanup(THEME_REC *rec) +{ + PyTheme *pytheme = signal_get_user_data(); + if (pytheme->data == rec) + { + pytheme->data = NULL; + pytheme->cleanup_installed = 0; + signal_remove_data("theme destroyed", theme_cleanup, pytheme); + } +} + +static void PyTheme_dealloc(PyTheme *self) +{ + if (self->cleanup_installed) + signal_remove_data("theme destroyed", theme_cleanup, self); + + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyTheme_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyTheme *self; + + self = (PyTheme *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* Getters */ +/* specialized getters/setters */ +static PyGetSetDef PyTheme_getseters[] = { + {NULL} +}; + +/* Methods */ +PyDoc_STRVAR(PyTheme_format_expand_doc, + "format_expand(format, flags=0) -> str or None\n" +); +static PyObject *PyTheme_format_expand(PyTheme *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"format", "flags", NULL}; + char *format = ""; + int flags = 0; + char *ret; + PyObject *pyret; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, + &format, &flags)) + return NULL; + + if (flags == 0) + ret = theme_format_expand(self->data, format); + else + ret = theme_format_expand_data(self->data, (const char **)&format, 'n', 'n', + NULL, NULL, EXPAND_FLAG_ROOT | flags); + + if (ret) + { + pyret = PyString_FromString(ret); + g_free(ret); + return pyret; + } + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyTheme_get_format_doc, + "get_format(module, tag) -> str\n" +); +static PyObject *PyTheme_get_format(PyTheme *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"module", "tag", NULL}; + char *module = ""; + char *tag = ""; + THEME_REC *theme = self->data; + FORMAT_REC *formats; + MODULE_THEME_REC *modtheme; + int i; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist, + &module, &tag)) + return NULL; + + formats = g_hash_table_lookup(default_formats, module); + if (!formats) + return PyErr_Format(PyExc_KeyError, "unknown module, %s", module); + + for (i = 0; formats[i].def; i++) + { + if (formats[i].tag && !g_strcasecmp(formats[i].tag, tag)) + { + modtheme = g_hash_table_lookup(theme->modules, module); + if (modtheme && modtheme->formats[i]) + return PyString_FromString(modtheme->formats[i]); + else + return PyString_FromString(formats[i].def); + } + } + + return PyErr_Format(PyExc_KeyError, "unknown format tag, %s", tag); +} + +/* Methods for object */ +static PyMethodDef PyTheme_methods[] = { + {"format_expand", (PyCFunction)PyTheme_format_expand, METH_VARARGS | METH_KEYWORDS, + PyTheme_format_expand_doc}, + {"get_format", (PyCFunction)PyTheme_get_format, METH_VARARGS | METH_KEYWORDS, + PyTheme_get_format_doc}, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyThemeType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Theme", /*tp_name*/ + sizeof(PyTheme), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyTheme_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyTheme_methods, /* tp_methods */ + 0, /* tp_members */ + PyTheme_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyTheme_new, /* tp_new */ +}; + +/* Theme factory function */ +PyObject *pytheme_new(void *td) +{ + PyTheme *pytheme; + + pytheme = py_inst(PyTheme, PyThemeType); + if (!pytheme) + return NULL; + + pytheme->data = td; + signal_add_last_data("theme destroyed", theme_cleanup, pytheme); + pytheme->cleanup_installed = 1; + + return (PyObject *)pytheme; +} + +int theme_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyThemeType) < 0) + return 0; + + Py_INCREF(&PyThemeType); + PyModule_AddObject(py_module, "Theme", (PyObject *)&PyThemeType); + + return 1; +} diff --git a/src/objects/theme-object.h b/src/objects/theme-object.h new file mode 100644 index 0000000..299d54d --- /dev/null +++ b/src/objects/theme-object.h @@ -0,0 +1,18 @@ +#ifndef _THEME_OBJECT_H_ +#define _THEME_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +typedef struct +{ + PyIrssiFinal_HEAD(void) +} PyTheme; + +extern PyTypeObject PyThemeType; + +int theme_object_init(void); +PyObject *pytheme_new(void *td); +#define pytheme_check(op) PyObject_TypeCheck(op, &PyThemeType) + +#endif diff --git a/src/objects/window-item-object.c b/src/objects/window-item-object.c new file mode 100644 index 0000000..2f18f7d --- /dev/null +++ b/src/objects/window-item-object.c @@ -0,0 +1,334 @@ +#include <Python.h> +#include "pymodule.h" +#include "base-objects.h" +#include "window-item-object.h" +#include "pyirssi.h" +#include "pycore.h" +#include "pyutils.h" +#include "factory.h" + +/* Dealloc is overridden by sub types */ + +PyDoc_STRVAR(PyWindowItem_server_doc, + "Active name for item" +); +static PyObject *PyWindowItem_server_get(PyWindowItem *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_OBJ_OR_NONE(self->server); +} + +PyDoc_STRVAR(PyWindowItem_name_doc, + "Name of the item" +); +static PyObject *PyWindowItem_name_get(PyWindowItem *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->visible_name); +} + +PyDoc_STRVAR(PyWindowItem_createtime_doc, + "Time the witem was created" +); +static PyObject *PyWindowItem_createtime_get(PyWindowItem *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromLong(self->data->createtime); +} + +PyDoc_STRVAR(PyWindowItem_data_level_doc, + "0=no new data, 1=text, 2=msg, 3=highlighted text" +); +static PyObject *PyWindowItem_data_level_get(PyWindowItem *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->data_level); +} + +PyDoc_STRVAR(PyWindowItem_hilight_color_doc, + "Color of the last highlighted text" +); +static PyObject *PyWindowItem_hilight_color_get(PyWindowItem *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->hilight_color); +} + +/* specialized getters/setters */ +static PyGetSetDef PyWindowItem_getseters[] = { + {"server", (getter)PyWindowItem_server_get, NULL, + PyWindowItem_server_doc, NULL}, + {"name", (getter)PyWindowItem_name_get, NULL, + PyWindowItem_name_doc, NULL}, + {"createtime", (getter)PyWindowItem_createtime_get, NULL, + PyWindowItem_createtime_doc, NULL}, + {"data_level", (getter)PyWindowItem_data_level_get, NULL, + PyWindowItem_data_level_doc, NULL}, + {"hilight_color", (getter)PyWindowItem_hilight_color_get, NULL, + PyWindowItem_hilight_color_doc, NULL}, + {NULL} +}; + +/* Methods */ +PyDoc_STRVAR(PyWindowItem_prnt_doc, + "prnt(str, level) -> None\n" + "\n" + "Print to window item\n" +); +static PyObject *PyWindowItem_prnt(PyWindowItem *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"str", "level", NULL}; + char *str; + int level = MSGLEVEL_CLIENTNOTICE; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, &str, &level)) + return NULL; + + printtext_string(self->data->server, self->data->visible_name, level, str); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindowItem_command_doc, + "command(cmd) -> None\n" + "\n" + "Send command to window item\n" +); +static PyObject *PyWindowItem_command(PyWindowItem *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"cmd", NULL}; + char *cmd; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &cmd)) + return NULL; + + py_command(cmd, self->data->server, self->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindowItem_window_doc, + "window() -> Window object or None\n" + "\n" + "Return parent window for window item\n" +); +static PyObject *PyWindowItem_window(PyWindowItem *self, PyObject *args) +{ + WINDOW_REC *win; + + RET_NULL_IF_INVALID(self->data); + + win = window_item_window(self->data); + if (win) + return pywindow_new(win); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindowItem_change_server_doc, + "change_server(server) -> None\n" + "\n" + "Change server for window item\n" +); +static PyObject *PyWindowItem_change_server(PyWindowItem *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"server", NULL}; + PyObject *server = NULL; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, + &server)) + return NULL; + + if (!pyserver_check(server)) + return PyErr_Format(PyExc_TypeError, "arg must be server"); + + window_item_change_server(self->data, ((PyServer*)server)->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindowItem_is_active_doc, + "is_active() -> bool\n" + "\n" + "Returns true if window item is active\n" +); +static PyObject *PyWindowItem_is_active(PyWindowItem *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + return PyBool_FromLong(window_item_is_active(self->data)); +} + +PyDoc_STRVAR(PyWindowItem_set_active_doc, + "set_active() -> None\n" + "\n" + "Set window item active\n" +); +static PyObject *PyWindowItem_set_active(PyWindowItem *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + window_item_set_active(window_item_window(self->data), self->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindowItem_activity_doc, + "activity(data_level, hilight_color) -> None\n" + "\n" +); +static PyObject *PyWindowItem_activity(PyWindowItem *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"data_level", "hilight_color", NULL}; + int data_level = 0; + char *hilight_color = NULL; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "is", kwlist, + &data_level, &hilight_color)) + return NULL; + + window_item_activity(self->data, data_level, hilight_color); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindowItem_destroy_doc, + "destroy() -> None\n" + "\n" + "Destroy channel or query\n" +); +static PyObject *PyWindowItem_destroy(PyWindowItem *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + window_item_destroy(self->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindowItem_get_dcc_doc, + "get_dcc() -> DccChat object or None\n" + "\n" + "If item is a query of a =nick, return DCC chat record of nick\n" +); +static PyObject *PyWindowItem_get_dcc(PyWindowItem *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + return py_irssi_new(self->data, 1); +} + +/* Methods for object */ +static PyMethodDef PyWindowItem_methods[] = { + {"prnt", (PyCFunction)PyWindowItem_prnt, METH_VARARGS | METH_KEYWORDS, + PyWindowItem_prnt_doc}, + {"command", (PyCFunction)PyWindowItem_command, METH_VARARGS | METH_KEYWORDS, + PyWindowItem_command_doc}, + {"window", (PyCFunction)PyWindowItem_window, METH_NOARGS, + PyWindowItem_window_doc}, + {"change_server", (PyCFunction)PyWindowItem_change_server, METH_VARARGS | METH_KEYWORDS, + PyWindowItem_change_server_doc}, + {"is_active", (PyCFunction)PyWindowItem_is_active, METH_NOARGS, + PyWindowItem_is_active_doc}, + {"set_active", (PyCFunction)PyWindowItem_set_active, METH_NOARGS, + PyWindowItem_set_active_doc}, + {"activity", (PyCFunction)PyWindowItem_activity, METH_VARARGS | METH_KEYWORDS, + PyWindowItem_activity_doc}, + {"destroy", (PyCFunction)PyWindowItem_destroy, METH_NOARGS, + PyWindowItem_destroy_doc}, + {"get_dcc", (PyCFunction)PyWindowItem_get_dcc, METH_NOARGS, + PyWindowItem_get_dcc_doc}, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyWindowItemType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "WindowItem", /*tp_name*/ + sizeof(PyWindowItem), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyWindowItem objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyWindowItem_methods, /* tp_methods */ + 0, /* tp_members */ + PyWindowItem_getseters, /* tp_getset */ + &PyIrssiChatBaseType, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + + +/* window item wrapper factory function */ +PyObject *pywindow_item_sub_new(void *witem, const char *name, PyTypeObject *subclass) +{ + WI_ITEM_REC *rec = witem; + PyWindowItem *pywitem = NULL; + PyObject *server; + + g_return_val_if_fail(witem != NULL, NULL); + + server = py_irssi_chat_new(rec->server, 1); + if (!server) + return NULL; + + pywitem = py_instp(PyWindowItem, subclass); + if (!pywitem) + return NULL; + + pywitem->data = witem; + pywitem->base_name = name; + pywitem->server = server; + + return (PyObject *)pywitem; +} + +PyObject *pywindow_item_new(void *witem) +{ + return pywindow_item_sub_new(witem, NULL, &PyWindowItemType); +} + +int window_item_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyWindowItemType) < 0) + return 0; + + Py_INCREF(&PyWindowItemType); + PyModule_AddObject(py_module, "WindowItem", (PyObject *)&PyWindowItemType); + + return 1; +} diff --git a/src/objects/window-item-object.h b/src/objects/window-item-object.h new file mode 100644 index 0000000..e205ebe --- /dev/null +++ b/src/objects/window-item-object.h @@ -0,0 +1,26 @@ +#ifndef _WITEM_OBJECT_H_ +#define _WITEM_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +#define PyWindowItem_HEAD(type) \ + PyIrssi_HEAD(type) \ + PyObject *server; + +/* forward */ +struct _WI_ITEM_REC; + +typedef struct +{ + PyWindowItem_HEAD(struct _WI_ITEM_REC) +} PyWindowItem; + +extern PyTypeObject PyWindowItemType; + +int window_item_object_init(void); +PyObject *pywindow_item_sub_new(void *witem, const char *name, PyTypeObject *subclass); +PyObject *pywindow_item_new(void *witem); +#define pywindow_item_check(op) PyObject_TypeCheck(op, &PyWindowItemType) + +#endif diff --git a/src/objects/window-object.c b/src/objects/window-object.c new file mode 100644 index 0000000..d5aa475 --- /dev/null +++ b/src/objects/window-object.c @@ -0,0 +1,678 @@ +#include <Python.h> +#include "pyirssi_irc.h" +#include "pymodule.h" +#include "window-object.h" +#include "factory.h" +#include "pycore.h" +#include "pyutils.h" + +/* monitor "window destroyed" signal */ +static void window_cleanup(WINDOW_REC *win) +{ + PyWindow *pywindow = signal_get_user_data(); + + if (win == pywindow->data) + { + pywindow->data = NULL; + pywindow->cleanup_installed = 0; + signal_remove_data("window destroyed", window_cleanup, pywindow); + } +} + +static void PyWindow_dealloc(PyWindow *self) +{ + if (self->cleanup_installed) + signal_remove_data("window destroyed", window_cleanup, self); + + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyWindow_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyWindow *self; + + self = (PyWindow *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* Getters */ +PyDoc_STRVAR(PyWindow_refnum_doc, + "Reference number" +); +static PyObject *PyWindow_refnum_get(PyWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->refnum); +} + +PyDoc_STRVAR(PyWindow_name_doc, + "Name" +); +static PyObject *PyWindow_name_get(PyWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->name); +} + +PyDoc_STRVAR(PyWindow_width_doc, + "Width" +); +static PyObject *PyWindow_width_get(PyWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->width); +} + +PyDoc_STRVAR(PyWindow_height_doc, + "Height" +); +static PyObject *PyWindow_height_get(PyWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->height); +} + +PyDoc_STRVAR(PyWindow_history_name_doc, + "Name of named historylist for this window" +); +static PyObject *PyWindow_history_name_get(PyWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->history_name); +} + +PyDoc_STRVAR(PyWindow_active_doc, + "Active window item" +); +static PyObject *PyWindow_active_get(PyWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return py_irssi_chat_new(self->data->active, 1); +} + +PyDoc_STRVAR(PyWindow_active_server_doc, + "Active server" +); +static PyObject *PyWindow_active_server_get(PyWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return py_irssi_chat_new(self->data->active_server, 1); +} + +PyDoc_STRVAR(PyWindow_servertag_doc, + "active_server must be either None or have this same tag" + "(unless there's items in this window). This is used by" + "/WINDOW SERVER -sticky" +); +static PyObject *PyWindow_servertag_get(PyWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->servertag); +} + +PyDoc_STRVAR(PyWindow_level_doc, + "Current window level" +); +static PyObject *PyWindow_level_get(PyWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->level); +} + +PyDoc_STRVAR(PyWindow_sticky_refnum_doc, + "True if reference number is sticky" +); +static PyObject *PyWindow_sticky_refnum_get(PyWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->sticky_refnum); +} + +PyDoc_STRVAR(PyWindow_data_level_doc, + "Current data level" +); +static PyObject *PyWindow_data_level_get(PyWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->data_level); +} + +PyDoc_STRVAR(PyWindow_hilight_color_doc, + "Current activity hilight color" +); +static PyObject *PyWindow_hilight_color_get(PyWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->hilight_color); +} + +PyDoc_STRVAR(PyWindow_last_timestamp_doc, + "Last time timestamp was written in window" +); +static PyObject *PyWindow_last_timestamp_get(PyWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromUnsignedLong(self->data->last_timestamp); +} + +PyDoc_STRVAR(PyWindow_last_line_doc, + "Last time text was written in window" +); +static PyObject *PyWindow_last_line_get(PyWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromUnsignedLong(self->data->last_line); +} + +PyDoc_STRVAR(PyWindow_theme_name_doc, + "Active theme in window, None = default" +); +static PyObject *PyWindow_theme_name_get(PyWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->theme_name); +} + +/* specialized getters/setters */ +static PyGetSetDef PyWindow_getseters[] = { + {"refnum", (getter)PyWindow_refnum_get, NULL, + PyWindow_refnum_doc, NULL}, + {"name", (getter)PyWindow_name_get, NULL, + PyWindow_name_doc, NULL}, + {"width", (getter)PyWindow_width_get, NULL, + PyWindow_width_doc, NULL}, + {"height", (getter)PyWindow_height_get, NULL, + PyWindow_height_doc, NULL}, + {"history_name", (getter)PyWindow_history_name_get, NULL, + PyWindow_history_name_doc, NULL}, + {"active", (getter)PyWindow_active_get, NULL, + PyWindow_active_doc, NULL}, + {"active_server", (getter)PyWindow_active_server_get, NULL, + PyWindow_active_server_doc, NULL}, + {"servertag", (getter)PyWindow_servertag_get, NULL, + PyWindow_servertag_doc, NULL}, + {"level", (getter)PyWindow_level_get, NULL, + PyWindow_level_doc, NULL}, + {"sticky_refnum", (getter)PyWindow_sticky_refnum_get, NULL, + PyWindow_sticky_refnum_doc, NULL}, + {"data_level", (getter)PyWindow_data_level_get, NULL, + PyWindow_data_level_doc, NULL}, + {"hilight_color", (getter)PyWindow_hilight_color_get, NULL, + PyWindow_hilight_color_doc, NULL}, + {"last_timestamp", (getter)PyWindow_last_timestamp_get, NULL, + PyWindow_last_timestamp_doc, NULL}, + {"last_line", (getter)PyWindow_last_line_get, NULL, + PyWindow_last_line_doc, NULL}, + {"theme_name", (getter)PyWindow_theme_name_get, NULL, + PyWindow_theme_name_doc, NULL}, + {NULL} +}; + +/* Methods */ +PyDoc_STRVAR(PyWindow_items_doc, + "items() -> list of WindowItem objects\n" + "\n" + "Return a list of items in window.\n" +); +static PyObject *PyWindow_items(PyWindow *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + return py_irssi_chatlist_new(self->data->items, 1); +} + +PyDoc_STRVAR(PyWindow_prnt_doc, + "prnt(str, level=MSGLEVEL_CLIENTNOTICE) -> None\n" + "\n" + "Print to window\n" +); +static PyObject *PyWindow_prnt(PyWindow *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"str", "level", NULL}; + char *str = ""; + int level = MSGLEVEL_CLIENTNOTICE; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, + &str, &level)) + return NULL; + + printtext_string_window(self->data, level, str); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindow_command_doc, + "command(cmd) -> None\n" + "\n" + "Send command to window\n" +); +static PyObject *PyWindow_command(PyWindow *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"cmd", NULL}; + char *cmd = ""; + WINDOW_REC *old; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &cmd)) + return NULL; + + old = active_win; + active_win = self->data; + py_command(cmd, active_win->active_server, active_win->active); + if (g_slist_find(windows, old) != NULL) + active_win = old; + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindow_item_add_doc, + "item_add(item, automatic=False) -> None\n" + "\n" + "Add window item\n" +); +static PyObject *PyWindow_item_add(PyWindow *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"item", "automatic", NULL}; + PyObject *item = NULL; + int automatic = 0; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|i", kwlist, + &item, &automatic)) + return NULL; + + if (!pywindow_item_check(item)) + return PyErr_Format(PyExc_TypeError, "item must be window item"); + + window_item_add(self->data, ((PyWindowItem*)item)->data, automatic); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindow_item_remove_doc, + "item_remove(item) -> None\n" + "\n" + "Remove window item\n" +); +static PyObject *PyWindow_item_remove(PyWindow *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"item", NULL}; + PyObject *item = NULL; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, + &item)) + return NULL; + + if (!pywindow_item_check(item)) + return PyErr_Format(PyExc_TypeError, "item must be window item"); + + window_item_remove(((PyWindowItem*)item)->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindow_item_destroy_doc, + "item_destroy(item) -> None\n" + "\n" + "Destroy window item\n" +); +static PyObject *PyWindow_item_destroy(PyWindow *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"item", NULL}; + PyObject *item = NULL; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, + &item)) + return NULL; + + if (!pywindow_item_check(item)) + return PyErr_Format(PyExc_TypeError, "item must be window item"); + + window_item_destroy(((PyWindowItem*)item)->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindow_item_prev_doc, + "item_prev() -> None\n" + "\n" + "Change to previous window item\n" +); +static PyObject *PyWindow_item_prev(PyWindow *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + window_item_prev(self->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindow_item_next_doc, + "item_next() -> None\n" + "\n" + "Change to next window item\n" +); +static PyObject *PyWindow_item_next(PyWindow *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + window_item_next(self->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindow_destroy_doc, + "destroy() -> None\n" + "\n" + "Destroy the window.\n" +); +static PyObject *PyWindow_destroy(PyWindow *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + window_destroy(self->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindow_set_active_doc, + "set_active() -> None\n" + "\n" + "Set window active.\n" +); +static PyObject *PyWindow_set_active(PyWindow *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + window_set_active(self->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindow_change_server_doc, + "change_server(server) -> None\n" + "\n" + "Change server in window\n" +); +static PyObject *PyWindow_change_server(PyWindow *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"server", NULL}; + PyObject *server = NULL; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, + &server)) + return NULL; + + if (!pyserver_check(server)) + return PyErr_Format(PyExc_TypeError, "arg must be server"); + + window_change_server(self->data, ((PyServer*)server)->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindow_set_refnum_doc, + "set_refnum(refnum) -> None\n" + "\n" + "Set window refnum\n" +); +static PyObject *PyWindow_set_refnum(PyWindow *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"refnum", NULL}; + int refnum = 0; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, + &refnum)) + return NULL; + + window_set_refnum(self->data, refnum); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindow_set_name_doc, + "set_name(name) -> None\n" + "\n" + "Set window name\n" +); +static PyObject *PyWindow_set_name(PyWindow *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"name", NULL}; + char *name = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &name)) + return NULL; + + window_set_name(self->data, name); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindow_set_history_doc, + "set_history(history) -> None\n" + "\n" + "Set window history\n" +); +static PyObject *PyWindow_set_history(PyWindow *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"history", NULL}; + char *history = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &history)) + return NULL; + + window_set_history(self->data, history); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindow_set_level_doc, + "set_level(level) -> None\n" + "\n" + "Set window level\n" +); +static PyObject *PyWindow_set_level(PyWindow *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"level", NULL}; + int level = 0; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, + &level)) + return NULL; + + window_set_level(self->data, level); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindow_activity_doc, + "activity(data_level, hilight_color) -> None\n" + "\n" +); +static PyObject *PyWindow_activity(PyWindow *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"data_level", "hilight_color", NULL}; + int data_level = 0; + char *hilight_color = NULL; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "i|s", kwlist, + &data_level, &hilight_color)) + return NULL; + + window_activity(self->data, data_level, hilight_color); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindow_get_active_name_doc, + "get_active_name() -> str or None\n" + "\n" + "Return active item's name, or if none is active, window's name.\n" +); +static PyObject *PyWindow_get_active_name(PyWindow *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(window_get_active_name(self->data)); +} + +PyDoc_STRVAR(PyWindow_item_find_doc, + "item_find(server, name) -> WindowItem or None\n" + "\n" + "Find window item that matches best to given arguments\n" +); +static PyObject *PyWindow_item_find(PyWindow *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"server", "name", NULL}; + PyObject *server = NULL; + char *name = ""; + WI_ITEM_REC *witem; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "Os", kwlist, + &server, &name)) + return NULL; + + if (!pyserver_check(server)) + return PyErr_Format(PyExc_TypeError, "arg 1 must be server"); + + witem = window_item_find_window(self->data, ((PyServer*)server)->data, name); + return py_irssi_chat_new(witem, 1); +} + +static PyMethodDef PyWindow_methods[] = { + {"items", (PyCFunction)PyWindow_items, METH_NOARGS, + PyWindow_items_doc}, + {"prnt", (PyCFunction)PyWindow_prnt, METH_VARARGS | METH_KEYWORDS, + PyWindow_prnt_doc}, + {"command", (PyCFunction)PyWindow_command, METH_VARARGS | METH_KEYWORDS, + PyWindow_command_doc}, + {"item_add", (PyCFunction)PyWindow_item_add, METH_VARARGS | METH_KEYWORDS, + PyWindow_item_add_doc}, + {"item_remove", (PyCFunction)PyWindow_item_remove, METH_VARARGS | METH_KEYWORDS, + PyWindow_item_remove_doc}, + {"item_destroy", (PyCFunction)PyWindow_item_destroy, METH_VARARGS | METH_KEYWORDS, + PyWindow_item_destroy_doc}, + {"item_prev", (PyCFunction)PyWindow_item_prev, METH_NOARGS, + PyWindow_item_prev_doc}, + {"item_next", (PyCFunction)PyWindow_item_next, METH_NOARGS, + PyWindow_item_next_doc}, + {"destroy", (PyCFunction)PyWindow_destroy, METH_NOARGS, + PyWindow_destroy_doc}, + {"set_active", (PyCFunction)PyWindow_set_active, METH_NOARGS, + PyWindow_set_active_doc}, + {"change_server", (PyCFunction)PyWindow_change_server, METH_VARARGS | METH_KEYWORDS, + PyWindow_change_server_doc}, + {"set_refnum", (PyCFunction)PyWindow_set_refnum, METH_VARARGS | METH_KEYWORDS, + PyWindow_set_refnum_doc}, + {"set_name", (PyCFunction)PyWindow_set_name, METH_VARARGS | METH_KEYWORDS, + PyWindow_set_name_doc}, + {"set_history", (PyCFunction)PyWindow_set_history, METH_VARARGS | METH_KEYWORDS, + PyWindow_set_history_doc}, + {"set_level", (PyCFunction)PyWindow_set_level, METH_VARARGS | METH_KEYWORDS, + PyWindow_set_level_doc}, + {"activity", (PyCFunction)PyWindow_activity, METH_VARARGS | METH_KEYWORDS, + PyWindow_activity_doc}, + {"get_active_name", (PyCFunction)PyWindow_get_active_name, METH_NOARGS, + PyWindow_get_active_name_doc}, + {"item_find", (PyCFunction)PyWindow_item_find, METH_VARARGS | METH_KEYWORDS, + PyWindow_item_find_doc}, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyWindowType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Window", /*tp_name*/ + sizeof(PyWindow), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyWindow_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyWindow objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyWindow_methods, /* tp_methods */ + 0, /* tp_members */ + PyWindow_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyWindow_new, /* tp_new */ +}; + + +/* window item wrapper factory function */ +PyObject *pywindow_new(void *win) +{ + PyWindow *pywindow; + + pywindow = py_inst(PyWindow, PyWindowType); + if (!pywindow) + return NULL; + + pywindow->data = win; + pywindow->cleanup_installed = 1; + signal_add_last_data("window destroyed", window_cleanup, pywindow); + + return (PyObject *)pywindow; +} + +int window_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyWindowType) < 0) + return 0; + + Py_INCREF(&PyWindowType); + PyModule_AddObject(py_module, "Window", (PyObject *)&PyWindowType); + + return 1; +} diff --git a/src/objects/window-object.h b/src/objects/window-object.h new file mode 100644 index 0000000..b9860af --- /dev/null +++ b/src/objects/window-object.h @@ -0,0 +1,21 @@ +#ifndef _WINDOW_OBJECT_H_ +#define _WINDOW_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +/* forward */ +struct _WINDOW_REC; + +typedef struct +{ + PyIrssiFinal_HEAD(struct _WINDOW_REC) +} PyWindow; + +extern PyTypeObject PyWindowType; + +int window_object_init(void); +PyObject *pywindow_new(void *win); +#define pywindow_check(op) PyObject_TypeCheck(op, &PyWindowType) + +#endif diff --git a/src/pyconstants.h b/src/pyconstants.h new file mode 100644 index 0000000..f898ded --- /dev/null +++ b/src/pyconstants.h @@ -0,0 +1,7 @@ +#ifndef _PYCONSTANTS_H_ +#define _PYCONSTANTS_H_ + +void pyconstants_init(void); + +#endif + diff --git a/src/pycore.c b/src/pycore.c new file mode 100644 index 0000000..f9e4c3d --- /dev/null +++ b/src/pycore.c @@ -0,0 +1,160 @@ +#include <Python.h> +#include <string.h> +#include <signal.h> +#include <assert.h> +#include "pyirssi.h" +#include "pycore.h" +#include "pyloader.h" +#include "pymodule.h" +#include "pysignals.h" +#include "pythemes.h" +#include "pystatusbar.h" +#include "pyconstants.h" +#include "factory.h" + +/*XXX: copy parse into utils */ +static void cmd_exec(const char *data) +{ + PyObject *co; + PyObject *ret; + PyObject *d; + PyObject *m; + char *cmd; + + if (!*data) + cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS); + + cmd = g_strconcat(data, "\n", NULL); + + m = PyImport_AddModule("__main__"); + if (!m) + goto error; + + d = PyModule_GetDict(m); + if (!d) + goto error; + + co = Py_CompileString(cmd, "<stdin>", Py_single_input); + if (!co) + goto error; + + ret = PyEval_EvalCode((PyCodeObject *)co, d, d); + Py_DECREF(co); + Py_XDECREF(ret); + +error: + g_free(cmd); + if (PyErr_Occurred()) + PyErr_Print(); +} + +static void cmd_load(const char *data) +{ + char **argv; + + argv = g_strsplit(data, " ", -1); + if (*argv == NULL || **argv == '\0') + { + g_strfreev(argv); + cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS); + } + + pyloader_load_script_argv(argv); + g_strfreev(argv); +} + +static void cmd_unload(const char *data) +{ + void *free_arg; + char *script; + + if (!cmd_get_params(data, &free_arg, 1, &script)) + return; + + if (*script == '\0') + cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + pyloader_unload_script(script); + + cmd_params_free(free_arg); +} + +static void cmd_list() +{ + char buf[128]; + GSList *list; + + list = pyloader_list(); + + g_snprintf(buf, sizeof(buf), "%-15s %s", "Name", "File"); + + if (list != NULL) + { + GSList *node; + + printtext_string(NULL, NULL, MSGLEVEL_CLIENTCRAP, buf); + for (node = list; node != NULL; node = node->next) + { + PY_LIST_REC *item = node->data; + g_snprintf(buf, sizeof(buf), "%-15s %s", item->name, item->file); + + printtext_string(NULL, NULL, MSGLEVEL_CLIENTCRAP, buf); + } + } + else + printtext_string(NULL, NULL, MSGLEVEL_CLIENTERROR, "No python scripts are loaded"); + + pyloader_list_destroy(&list); +} + +#if 0 +/* why doesn't this get called? */ +static void intr_catch(int sig) +{ + printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "got sig %d", sig); + PyErr_SetInterrupt(); +} +#endif + +void irssi_python_init(void) +{ + Py_InitializeEx(0); + + pysignals_init(); + pystatusbar_init(); + if (!pyloader_init() || !pymodule_init() || !factory_init() || !pythemes_init()) + { + printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Failed to load Python"); + return; + } + pyconstants_init(); + + /*PyImport_ImportModule("irssi_startup");*/ + /* Install the custom output handlers, import hook and reload function */ + /* XXX: handle import error */ + PyRun_SimpleString( + "import irssi_startup\n" + ); + + //assert(signal(SIGINT, intr_catch) != SIG_ERR); + + command_bind("pyload", NULL, (SIGNAL_FUNC) cmd_load); + command_bind("pyunload", NULL, (SIGNAL_FUNC) cmd_unload); + command_bind("pylist", NULL, (SIGNAL_FUNC) cmd_list); + command_bind("pyexec", NULL, (SIGNAL_FUNC) cmd_exec); + module_register(MODULE_NAME, "core"); +} + +void irssi_python_deinit(void) +{ + command_unbind("pyload", (SIGNAL_FUNC) cmd_load); + command_unbind("pyunload", (SIGNAL_FUNC) cmd_unload); + command_unbind("pylist", (SIGNAL_FUNC) cmd_list); + command_unbind("pyexec", (SIGNAL_FUNC) cmd_exec); + + pymodule_deinit(); + pyloader_deinit(); + pystatusbar_deinit(); + pysignals_deinit(); + Py_Finalize(); +} diff --git a/src/pycore.h b/src/pycore.h new file mode 100644 index 0000000..ae51924 --- /dev/null +++ b/src/pycore.h @@ -0,0 +1,7 @@ +#ifndef _PYCORE_H +#define _PYCORE_H + +void irssi_python_init(void); +void irssi_python_deinit(void); + +#endif diff --git a/src/pyirssi.h b/src/pyirssi.h new file mode 100644 index 0000000..3fc4223 --- /dev/null +++ b/src/pyirssi.h @@ -0,0 +1,32 @@ +#ifndef _PYIRSSI_H_ +#define _PYIRSSI_H_ + +#define MODULE_NAME "irssi_python" +#include "config.h" +#include "core.h" +#include "common.h" +#include "modules.h" +#include "commands.h" +#include "settings.h" +#include "printtext.h" +#include "statusbar.h" +#include "mainwindows.h" +#include "window-items.h" +#include "window-activity.h" +#include "levels.h" +#include "servers.h" +#include "chat-protocols.h" +#include "channels.h" +#include "queries.h" +#include "nicklist.h" +#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 diff --git a/src/pyirssi_irc.h b/src/pyirssi_irc.h new file mode 100644 index 0000000..0ebd617 --- /dev/null +++ b/src/pyirssi_irc.h @@ -0,0 +1,20 @@ +#ifndef _PY_IRSSI_IRC_H_ +#define _PY_IRSSI_IRC_H_ + +#include "pyirssi.h" +#include "irc.h" +#include "irc-servers.h" +#include "servers-redirect.h" +#include "irc-channels.h" +#include "ctcp.h" +#include "mode-lists.h" +#include "bans.h" +#include "dcc.h" +#include "dcc-get.h" +#include "dcc-send.h" +#include "dcc-chat.h" +#include "netsplit.h" +#include "notifylist.h" +#include "irc-masks.h" + +#endif diff --git a/src/pyloader.c b/src/pyloader.c new file mode 100644 index 0000000..a087827 --- /dev/null +++ b/src/pyloader.c @@ -0,0 +1,358 @@ +#include <Python.h> +#include <frameobject.h> +#include <string.h> +#include "pyirssi.h" +#include "pyloader.h" +#include "pyutils.h" +#include "pyscript-object.h" + +/* List of loaded modules */ +static PyObject *script_modules; + +/* List of load paths for scripts */ +static GSList *script_paths = NULL; + +static PyObject *py_get_script(const char *name, int *id); +static int py_load_module(PyObject *module, const char *path); +static char *py_find_script(const char *name); + +/* Add to the list of script load paths */ +void pyloader_add_script_path(const char *path) +{ + PyObject *ppath = PySys_GetObject("path"); + if (ppath) + { + PyList_Append(ppath, PyString_FromString(path)); + script_paths = g_slist_append(script_paths, g_strdup(path)); + } +} + +/* Loads a file into a module; it is not inserted into sys.modules */ +static int py_load_module(PyObject *module, const char *path) +{ + PyObject *dict, *ret, *fp; + + if (PyModule_AddStringConstant(module, "__file__", (char *)path) < 0) + return 0; + + dict = PyModule_GetDict(module); + + if (PyDict_SetItemString(dict, "__builtins__", PyEval_GetBuiltins()) < 0) + return 0; + + /* Dont use the standard library to avoid incompatabilities with + the FILE structure and Python */ + fp = PyFile_FromString((char *)path, "r"); + if (!fp) + return 0; + + ret = PyRun_File(PyFile_AsFile(fp), path, Py_file_input, dict, dict); + Py_DECREF(fp); /* XXX: I assume that the file is closed when refs drop to zero? */ + if (!ret) + return 0; + + Py_DECREF(ret); + return 1; + +} + +/* looks up name in Irssi script directories + returns full path or NULL if not found */ +static char *py_find_script(const char *name) +{ + GSList *node; + char *fname; + char *path = NULL; + + //XXX: what if there's another ext? + if (!file_has_ext(name, "py")) + fname = g_strdup_printf("%s.py", name); + else + fname = (char *)name; + + /*XXX: use case insensitive path search? */ + for (node = script_paths; node && !path; node = node->next) + { + path = g_strdup_printf("%s/%s", (char *)node->data, fname); + + if (!g_file_test(path, G_FILE_TEST_IS_REGULAR)) + { + g_free(path); + path = NULL; + } + } + + if (fname != name) + g_free(fname); + + return path; +} + +/* Load a script manually using PyRun_File. + * This expects a null terminated array of strings + * (such as from g_strsplit) of the command line. + * The array needs at least one item + */ +int pyloader_load_script_argv(char **argv) +{ + PyObject *module = NULL, *script = NULL; + char *name = NULL, *path = NULL; + + if (py_get_script(argv[0], NULL) != NULL) + { + printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "script %s already loaded", argv[0]); + return 0; + } + + path = py_find_script(argv[0]); + if (!path) + { + printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "script %s does not exist", argv[0]); + return 0; + } + + 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) + goto error; + + Py_INCREF(script); + + if (!py_load_module(module, path)) + goto error; + + 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); */ + + Py_DECREF(script); + g_free(path); + + return 1; + +error: + if (PyErr_Occurred()) + PyErr_Print(); + else + printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "error loading script %s", argv[0]); + + if (script) + { + /* make sure to clean up any formats, signals, commands that may have been + attached before the exception took place */ + pyscript_cleanup(script); + Py_DECREF(script); + } + + g_free(path); + + return 0; +} + +int pyloader_load_script(char *name) +{ + char *argv[2]; + + argv[0] = name; + argv[1] = NULL; + + return pyloader_load_script_argv(argv); +} + +static PyObject *py_get_script(const char *name, int *id) +{ + int i; + + g_return_val_if_fail(script_modules != NULL, NULL); + + for (i = 0; i < PyList_Size(script_modules); i++) + { + PyObject *script; + char *sname; + + script = PyList_GET_ITEM(script_modules, i); + sname = pyscript_get_name(script); + + if (sname && !strcmp(sname, name)) + { + if (id) + *id = i; + return script; + } + } + + return NULL; +} + +int pyloader_unload_script(const char *name) +{ + int id; + PyObject *script = py_get_script(name, &id); + + if (!script) + { + printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s is not loaded", name); + return 0; + } + + PySys_WriteStdout("unload %s, script -> 0x%x\n", name, script); + + pyscript_cleanup(script); + + if (PySequence_DelItem(script_modules, id) < 0) + { + PyErr_Print(); + printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "error unloading script %s", name); + return 0; + } + + /* Probably a good time to call the garbage collecter to clean up reference cycles */ + PyGC_Collect(); + printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "unloaded script %s", 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; + GSList *list = NULL; + + g_return_val_if_fail(script_modules != NULL, NULL); + + for (i = 0; i < PyList_Size(script_modules); i++) + { + PyObject *scr; + char *name, *file; + + scr = PyList_GET_ITEM(script_modules, i); + name = pyscript_get_name(scr); + file = pyscript_get_filename(scr); + + if (name && file) + { + PY_LIST_REC *rec; + rec = g_new0(PY_LIST_REC, 1); + + rec->name = g_strdup(name); + rec->file = g_strdup(file); + list = g_slist_append(list, rec); + } + } + + return list; +} + +void pyloader_list_destroy(GSList **list) +{ + GSList *node; + + if (*list == NULL) + return; + + for (node = *list; node != NULL; node = node->next) + { + PY_LIST_REC *rec = node->data; + + g_free(rec->name); + g_free(rec->file); + g_free(rec); + } + + g_slist_free(*list); + + *list = NULL; +} + +int pyloader_init(void) +{ + char *pyhome; + + g_return_val_if_fail(script_paths == NULL, 0); + g_return_val_if_fail(script_modules == NULL, 0); + + script_modules = PyList_New(0); + if (!script_modules) + return 0; + + /* XXX: load autorun scripts here */ + /* Add script location to the load path (add more paths later) */ + pyhome = g_strdup_printf("%s/scripts", get_irssi_dir()); + pyloader_add_script_path(pyhome); + g_free(pyhome); + + 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_cleanup(scr); + } + + Py_DECREF(script_modules); +} + +void pyloader_deinit(void) +{ + GSList *node; + + g_return_if_fail(script_paths != NULL); + g_return_if_fail(script_modules != NULL); + + for (node = script_paths; node != NULL; node = node->next) + g_free(node->data); + g_slist_free(script_paths); + script_paths = NULL; + + py_clear_scripts(); +} diff --git a/src/pyloader.h b/src/pyloader.h new file mode 100644 index 0000000..b7befd3 --- /dev/null +++ b/src/pyloader.h @@ -0,0 +1,23 @@ +#ifndef _PYLOADER_H_ +#define _PYLOADER_H_ + +typedef struct +{ + char *name; + char *file; +} PY_LIST_REC; + +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); + +int pyloader_init(void); +void pyloader_deinit(void); + +#endif diff --git a/src/pymodule.c b/src/pymodule.c new file mode 100644 index 0000000..7795c1a --- /dev/null +++ b/src/pymodule.c @@ -0,0 +1,1909 @@ +#include <Python.h> +#include "pymodule.h" +#include "pyirssi_irc.h" +#include "factory.h" +#include "pyutils.h" +#include "pysignals.h" +#include "pyloader.h" +#include "pythemes.h" +#include "pystatusbar.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_add, etc require data to be saved about the script + * for cleanup purposes, so I moved those functions to the script + * object. + */ + +/* Main embedded module */ +PyObject *py_module = NULL; + +/* Module functions */ +/*XXX: prefix PY to avoid ambiguity with py_command function */ +PyDoc_STRVAR(PY_command_doc, + "command(cmd) -> None\n" + "\n" + "Execute command\n" +); +static PyObject *PY_command(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"cmd", NULL}; + char *cmd = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &cmd)) + return NULL; + + py_command(cmd, NULL, NULL); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(py_prnt_doc, + "prnt(text, msglvl=MSGLEVEL_CLIENTNOTICE) -> None\n" + "\n" + "print output\n" +); +/*XXX: print is a python keyword, so abbreviate it */ +static PyObject *py_prnt(PyObject *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"text", "msglvl", NULL}; + int msglvl = MSGLEVEL_CLIENTNOTICE; + char *text = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|i:prnt", kwlist, + &text, &msglvl)) + return NULL; + + printtext_string(NULL, NULL, msglvl, text); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(py_get_script_doc, + "get_script() -> Script object\n" + "\n" + "Find current Irssi script object\n" +); +static PyObject *py_get_script(PyObject *self, PyObject *args) +{ + PyObject *ret = pyloader_find_script_obj(); + + /* XXX: type check */ + + if (!ret) + PyErr_SetString(PyExc_RuntimeError, "unable to find script object"); + else + Py_INCREF(ret); + + return ret; +} + +PyDoc_STRVAR(py_chatnet_find_doc, + "chatnet_find(name) -> Chatnet object or None\n" + "\n" + "Find chat network with name\n" +); +static PyObject *py_chatnet_find(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"name", NULL}; + char *name = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &name)) + return NULL; + + return py_irssi_chat_new(chatnet_find(name), 1); +} + +PyDoc_STRVAR(py_chatnets_doc, + "chatnets() -> list of Chatnet objects\n" + "\n" + "Return a list of all chatnets\n" +); +static PyObject *py_chatnets(PyObject *self, PyObject *args) +{ + return py_irssi_chatlist_new(chatnets, 1); +} + +PyDoc_STRVAR(py_reconnects_doc, + "reconnects() -> list of Reconnect objects\n" + "\n" + "Return a list of all reconnects\n" +); +static PyObject *py_reconnects(PyObject *self, PyObject *args) +{ + return py_irssi_objlist_new(reconnects, 1, (InitFunc)pyreconnect_new); +} + +PyDoc_STRVAR(py_servers_doc, + "servers() -> list of Server objects\n" + "\n" + "Return a list of all servers\n" +); +static PyObject *py_servers(PyObject *self, PyObject *args) +{ + return py_irssi_chatlist_new(servers, 1); +} + +PyDoc_STRVAR(py_channels_doc, + "channels() -> list of Channel objects\n" + "\n" + "Return channel list\n" +); +static PyObject *py_channels(PyObject *self, PyObject *args) +{ + return py_irssi_chatlist_new(channels, 1); +} + +PyDoc_STRVAR(py_channel_find_doc, + "channel_find(name) -> Channel object or None\n" + "\n" + "Find channel from any server\n" +); +static PyObject *py_channel_find(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"name", NULL}; + char *name = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &name)) + return NULL; + + return py_irssi_chat_new(channel_find(NULL, name), 1); +} + +PyDoc_STRVAR(py_windows_doc, + "windows() -> list of Window objects\n" + "\n" + "Get a list of all windows\n" +); +static PyObject *py_windows(PyObject *self, PyObject *args) +{ + return py_irssi_objlist_new(windows, 1, (InitFunc)pywindow_new); +} + +PyDoc_STRVAR(py_active_win_doc, + "active_win() -> Window object\n" + "\n" + "Return active window\n" +); +static PyObject *py_active_win(PyObject *self, PyObject *args) +{ + if (active_win) + return pywindow_new(active_win); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(py_active_server_doc, + "active_server() -> Server object or None\n" + "\n" + "Return active server\n" +); +static PyObject *py_active_server(PyObject *self, PyObject *args) +{ + if (active_win) + return py_irssi_chat_new(active_win->active_server, 1); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(py_window_find_name_doc, + "window_find_name(name) -> Window object or None\n" + "\n" + "Find window with name\n" +); +static PyObject *py_window_find_name(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"name", NULL}; + char *name = ""; + WINDOW_REC *win; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &name)) + return NULL; + + win = window_find_name(name); + if (win) + return pywindow_new(win); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(py_window_find_refnum_doc, + "window_find_refnum(refnum) -> Window object or None\n" + "\n" + "Find window with reference number\n" +); +static PyObject *py_window_find_refnum(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"refnum", NULL}; + int refnum = 0; + WINDOW_REC *win; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, + &refnum)) + return NULL; + + win = window_find_refnum(refnum); + if (win) + return pywindow_new(win); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(py_window_refnum_prev_doc, + "window_refnum_prev(refnum, wrap) -> int\n" + "\n" + "Return refnum for window that's previous in window list\n" +); +static PyObject *py_window_refnum_prev(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"refnum", "wrap", NULL}; + int refnum = 0; + int wrap = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ii", kwlist, + &refnum, &wrap)) + return NULL; + + return PyInt_FromLong(window_refnum_prev(refnum, wrap)); +} + +PyDoc_STRVAR(py_window_refnum_next_doc, + "window_refnum_next(refnum, wrap) -> int\n" + "\n" + "Return refnum for window that's next in window list\n" +); +static PyObject *py_window_refnum_next(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"refnum", "wrap", NULL}; + int refnum = 0; + int wrap = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ii", kwlist, + &refnum, &wrap)) + return NULL; + + return PyInt_FromLong(window_refnum_next(refnum, wrap)); +} + +PyDoc_STRVAR(py_windows_refnum_last_doc, + "windows_refnum_last() -> int\n" + "\n" + "Return refnum for last window.\n" +); +static PyObject *py_windows_refnum_last(PyObject *self, PyObject *args) +{ + return PyInt_FromLong(windows_refnum_last()); +} + +PyDoc_STRVAR(py_window_find_level_doc, + "window_find_level(level) -> Window object or None\n" + "\n" + "Find window with level.\n" +); +static PyObject *py_window_find_level(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"level", NULL}; + int level = 0; + WINDOW_REC *win; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, + &level)) + return NULL; + + win = window_find_level(NULL, level); + if (win) + return pywindow_new(win); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(py_window_find_item_doc, + "window_find_item(name) -> Window object or None\n" + "\n" + "Find window which contains window item with specified name.\n" +); +static PyObject *py_window_find_item(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"name", NULL}; + char *name = ""; + WINDOW_REC *win; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &name)) + return NULL; + + win = window_find_item(NULL, name); + if (win) + return pywindow_new(win); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(py_window_find_closest_doc, + "window_find_closest(name, level) -> Window object or None\n" + "\n" + "Find window that matches best to given arguments. `name' can be either\n" + "window name or name of one of the window items.\n" +); +static PyObject *py_window_find_closest(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"name", "level", NULL}; + char *name = ""; + int level = 0; + WINDOW_REC *win; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "si", kwlist, + &name, &level)) + return NULL; + + win = window_find_closest(NULL, name, level); + if (win) + return pywindow_new(win); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(py_window_item_find_doc, + "window_item_find(name) -> WindowItem object or None\n" + "\n" + "Find window item that matches best to given arguments.\n" +); +static PyObject *py_window_item_find(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"name", NULL}; + char *name = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &name)) + return NULL; + + return py_irssi_chat_new(window_item_find(NULL, name), 1); +} + +/*XXX: this could be __init__ for Window */ +PyDoc_STRVAR(py_window_create_doc, + "window_create(item=None, automatic=False) -> Window object\n" + "\n" + "Create a new window\n" +); +static PyObject *py_window_create(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"item", "automatic", NULL}; + PyObject *item = NULL; + int automatic = 0; + WI_ITEM_REC *witem = NULL; + WINDOW_REC *win; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oi", kwlist, + &item, &automatic)) + return NULL; + + if (item) + { + if (pywindow_item_check(item)) + { + witem = ((PyWindowItem*)item)->data; + if (!witem) + return PyErr_Format(PyExc_TypeError, "invalid window item"); + else if (witem->server != NULL) + return PyErr_Format(PyExc_TypeError, "window item already assigned to window"); + } + else if (item == Py_None) + ; + else + return PyErr_Format(PyExc_TypeError, "item must be window item or None"); + } + + win = window_create(witem, automatic); + if (win) + return pywindow_new(win); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(py_server_find_tag_doc, + "server_find_tag(tag) -> Server object or None\n" + "\n" + "Find server with tag\n" +); +static PyObject *py_server_find_tag(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"tag", NULL}; + char *tag = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &tag)) + return NULL; + + return py_irssi_chat_new(server_find_tag(tag), 1); +} + +PyDoc_STRVAR(py_server_find_chatnet_doc, + "server_find_chatnet(chatnet) -> Server object or None\n" + "\n" + "Find first server that is in chatnet\n" +); +static PyObject *py_server_find_chatnet(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"chatnet", NULL}; + char *chatnet = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &chatnet)) + return NULL; + + return py_irssi_chat_new(server_find_chatnet(chatnet), 1); +} + +PyDoc_STRVAR(py_queries_doc, + "queries() -> list of Query objects\n" + "\n" + "Return a list of open queries.\n" +); +static PyObject *py_queries(PyObject *self, PyObject *args) +{ + return py_irssi_chatlist_new(queries, 1); +} + +PyDoc_STRVAR(py_query_find_doc, + "query_find(nick) -> Query object or None\n" + "\n" + "Find a query from any server.\n" +); +static PyObject *py_query_find(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"nick", NULL}; + char *nick = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &nick)) + return NULL; + + return py_irssi_chat_new(query_find(NULL, nick), 1); +} + +PyDoc_STRVAR(py_mask_match_doc, + "mask_match(mask, nick, user, host) -> bool\n" + "\n" + "Return true if mask matches nick!user@host\n" +); +static PyObject *py_mask_match(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"mask", "nick", "user", "host", NULL}; + char *mask = ""; + char *nick = ""; + char *user = ""; + char *host = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ssss", kwlist, + &mask, &nick, &user, &host)) + return NULL; + + return PyBool_FromLong(mask_match(NULL, mask, nick, user, host)); +} + +PyDoc_STRVAR(py_mask_match_address_doc, + "mask_match_address(mask, nick, address) -> bool\n" + "\n" + "Return True if mask matches nick!address\n" +); +static PyObject *py_mask_match_address(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"mask", "nick", "address", NULL}; + char *mask = ""; + char *nick = ""; + char *address = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "sss", kwlist, + &mask, &nick, &address)) + return NULL; + + return PyBool_FromLong(mask_match_address(NULL, mask, nick, address)); +} + +PyDoc_STRVAR(py_masks_match_doc, + "masks_match(masks, nick, address) -> bool\n" + "\n" + "Return True if any mask in the masks (string separated by spaces)\n" + "matches nick!address\n" +); +static PyObject *py_masks_match(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"masks", "nick", "address", NULL}; + char *masks = ""; + char *nick = ""; + char *address = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "sss", kwlist, + &masks, &nick, &address)) + return NULL; + + return PyBool_FromLong(masks_match(NULL, masks, nick, address)); +} + +PyDoc_STRVAR(py_rawlog_set_size_doc, + "rawlog_set_size(lines) -> None\n" + "\n" + "Set the default rawlog size for new rawlogs.\n" +); +static PyObject *py_rawlog_set_size(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"lines", NULL}; + int lines = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, + &lines)) + return NULL; + + rawlog_set_size(lines); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(py_logs_doc, + "logs() -> list of Log objects\n" + "\n" + "Return list of logs.\n" +); +static PyObject *py_logs(PyObject *self, PyObject *args) +{ + return py_irssi_objlist_new(logs, 1, (InitFunc)pylog_new); +} + +PyDoc_STRVAR(py_log_find_doc, + "log_find(fname) -> Log object or None\n" + "\n" + "Find log by file name.\n" +); +static PyObject *py_log_find(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"fname", NULL}; + char *fname = ""; + LOG_REC *log; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &fname)) + return NULL; + + log = log_find(fname); + if (log) + return pylog_new(log); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(py_ignores_doc, + "ignores() -> list of Ignore objects\n" + "\n" + "Return a list of ignore entries\n" +); +static PyObject *py_ignores(PyObject *self, PyObject *args) +{ + return py_irssi_objlist_new(ignores, 1, (InitFunc)pyignore_new); +} + +PyDoc_STRVAR(py_ignore_check_doc, + "ignore_check(nick, host, channel, text, level) -> bool\n" + "\n" + "Return True if ignore matches\n" +); +static PyObject *py_ignore_check(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"nick", "host", "channel", "text", "level", NULL}; + char *nick = ""; + char *host = ""; + char *channel = ""; + char *text = ""; + int level = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ssssi", kwlist, + &nick, &host, &channel, &text, &level)) + return NULL; + + return PyBool_FromLong(ignore_check(NULL, nick, host, channel, text, level)); +} + +PyDoc_STRVAR(py_dccs_doc, + "dccs() -> list of Dcc objects\n" + "\n" + "Return list of active DCCs\n" +); +static PyObject *py_dccs(PyObject *self, PyObject *args) +{ + return py_irssi_list_new(dcc_conns, 1); +} + +PyDoc_STRVAR(py_dcc_register_type_doc, + "dcc_register_type(type) -> None\n" + "\n" + "???\n" +); +static PyObject *py_dcc_register_type(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"type", NULL}; + char *type = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &type)) + return NULL; + + dcc_register_type(type); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(py_dcc_unregister_type_doc, + "dcc_unregister_type(type) -> None\n" + "\n" + "???\n" +); +static PyObject *py_dcc_unregister_type(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"type", NULL}; + char *type = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &type)) + return NULL; + + dcc_unregister_type(type); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(py_dcc_find_request_latest_doc, + "dcc_find_request_latest(type) -> Dcc object or None\n" + "\n" + "???\n" +); +static PyObject *py_dcc_find_request_latest(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"type", NULL}; + int type = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, + &type)) + return NULL; + + return py_irssi_new(dcc_find_request_latest(type), 1); +} + +PyDoc_STRVAR(py_dcc_find_request_doc, + "dcc_find_request(type, nick, arg) -> Dcc object or None\n" + "\n" + "???\n" +); +static PyObject *py_dcc_find_request(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"type", "nick", "arg", NULL}; + int type = 0; + char *nick = ""; + char *arg = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "iss", kwlist, + &type, &nick, &arg)) + return NULL; + + return py_irssi_new(dcc_find_request(type, nick, arg), 1); +} + +PyDoc_STRVAR(py_dcc_chat_find_id_doc, + "dcc_chat_find_id(id) -> Dcc object or None\n" + "\n" + "???\n" +); +static PyObject *py_dcc_chat_find_id(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"id", NULL}; + char *id = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &id)) + return NULL; + + return py_irssi_new(dcc_chat_find_id(id), 1); +} + +PyDoc_STRVAR(py_dcc_str2type_doc, + "dcc_str2type(type) -> int\n" + "\n" + "???\n" +); +static PyObject *py_dcc_str2type(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"type", NULL}; + char *type = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &type)) + return NULL; + + return PyInt_FromLong(dcc_str2type(type)); +} + +PyDoc_STRVAR(py_dcc_type2str_doc, + "dcc_type2str(type) -> str or None\n" + "\n" + "???\n" +); +static PyObject *py_dcc_type2str(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"type", NULL}; + int type = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, + &type)) + return NULL; + + RET_AS_STRING_OR_NONE(dcc_type2str(type)); +} + +PyDoc_STRVAR(py_dcc_get_download_path_doc, + "dcc_get_download(fname) -> str\n" + "\n" + "???\n" +); +static PyObject *py_dcc_get_download_path(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"fname", NULL}; + char *fname = ""; + char *path; + PyObject *pypath; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &fname)) + return NULL; + + path = dcc_get_download_path(fname); + if (!path) + Py_RETURN_NONE; /*XXX: how to handle? */ + + pypath = PyString_FromString(path); + g_free(path); + + return pypath; +} + +PyDoc_STRVAR(py_notifies_doc, + "notifies() -> list of Notifylist objects\n" + "\n" + "Return list of notifies\n" +); +static PyObject *py_notifies(PyObject *self, PyObject *args) +{ + return py_irssi_objlist_new(notifies, 1, (InitFunc)pynotifylist_new); +} + +PyDoc_STRVAR(py_notifylist_add_doc, + "notifylist_add(mask, ircnets=None, away_check=0, idle_time_check=0) -> Notifylist object\n" + "\n" + "Add new item to notify list\n" +); +static PyObject *py_notifylist_add(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"mask", "ircnets", "away_check", "idle_check_time", NULL}; + char *mask = ""; + char *ircnets = NULL; + int away_check = 0; + int idle_check_time = 0; + NOTIFYLIST_REC *rec; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|zii", kwlist, + &mask, &ircnets, &away_check, &idle_check_time)) + return NULL; + + rec = notifylist_add(mask, ircnets, away_check, idle_check_time); + if (rec) + return pynotifylist_new(rec); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(py_notifylist_remove_doc, + "notifylist_remove(mask) -> None\n" + "\n" + "Remove notify item from notify list\n" +); +static PyObject *py_notifylist_remove(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"mask", NULL}; + char *mask = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &mask)) + return NULL; + + notifylist_remove(mask); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(py_notifylist_ison_doc, + "notifylist_ison(nick, serverlist=\"\") -> IrcServer object\n" + "\n" + "Check if nick is in IRC. serverlist is a space separated list of server tags.\n" + "If it's empty string, all servers will be checked\n" +); +static PyObject *py_notifylist_ison(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"nick", "serverlist", NULL}; + char *nick = ""; + char *serverlist = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|s", kwlist, + &nick, &serverlist)) + return NULL; + + return py_irssi_chat_new(notifylist_ison(nick, serverlist), 1); +} + +PyDoc_STRVAR(py_notifylist_find_doc, + "notifylist_find(mask, ircnet=None) -> Notifylist object\n" + "\n" + "Find notify\n" +); +static PyObject *py_notifylist_find(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"mask", "ircnet", NULL}; + char *mask = ""; + char *ircnet = NULL; + NOTIFYLIST_REC *rec; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|z", kwlist, + &mask, &ircnet)) + return NULL; + + rec = notifylist_find(mask, ircnet); + if (rec) + return pynotifylist_new(rec); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(py_commands_doc, + "commands() -> list of Command objects\n" + "\n" + "Return a list of all commands.\n" +); +static PyObject *py_commands(PyObject *self, PyObject *args) +{ + return py_irssi_objlist_new(commands, 1, (InitFunc)pycommand_new); +} + +PyDoc_STRVAR(py_level2bits_doc, + "level2bits(level) -> long\n" + "\n" + "Level string -> number\n" +); +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, + "bits2level(bits) -> str\n" + "\n" + "Level number -> string\n" +); +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(level, str) -> long\n" + "\n" + "Combine level number to level string ('+level -level'). Return new level number.\n" +); +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)); +} + +PyDoc_STRVAR(py_signal_emit_doc, + "signal_emit(signal, *args) -> None\n" + "\n" + "Emit an Irssi signal with up to 6 arguments\n" +); +static PyObject *py_signal_emit(PyObject *self, PyObject *args) +{ + PyObject *pysig; + PyObject *sigargs; + char *name; + int ret; + + if (PyTuple_Size(args) < 1) + return PyErr_Format(PyExc_TypeError, "signal name required"); + + if (PyTuple_Size(args) > SIGNAL_MAX_ARGUMENTS+1) + return PyErr_Format(PyExc_TypeError, + "no more than %d arguments for signal accepted", SIGNAL_MAX_ARGUMENTS); + + pysig = PyTuple_GET_ITEM(args, 0); + if (!PyString_Check(pysig)) + return PyErr_Format(PyExc_TypeError, "signal must be string"); + + name = PyString_AsString(pysig); + if (!name) + return NULL; + + sigargs = PySequence_GetSlice(args, 1, PyTuple_Size(args)); + if (!sigargs) + return NULL; + + ret = pysignals_emit(name, sigargs); + Py_DECREF(sigargs); + if (!ret) + return NULL; + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(py_signal_continue_doc, + "signal_continue(*args) -> None\n" + "\n" + "Continue (reemit?) the current Irssi signal with up to 6 arguments\n" +); +static PyObject *py_signal_continue(PyObject *self, PyObject *args) +{ + if (PyTuple_Size(args) > SIGNAL_MAX_ARGUMENTS) + return PyErr_Format(PyExc_TypeError, + "no more than %d arguments for signal accepted", SIGNAL_MAX_ARGUMENTS); + + if (!pysignals_continue(args)) + return NULL; + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(py_signal_stop_doc, + "signal_stop() -> None\n" + "\n" + "Stop the signal that's currently being emitted.\n" +); +static PyObject *py_signal_stop(PyObject *self, PyObject *args) +{ + signal_stop(); + Py_RETURN_NONE; +} + +PyDoc_STRVAR(py_signal_stop_by_name_doc, + "signal_stop_by_name(signal) -> None\n" + "\n" + "Stop the signal, 'signal', thats currently being emitted by name\n" +); +static PyObject *py_signal_stop_by_name(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"signal", NULL}; + char *signal = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &signal)) + return NULL; + + signal_stop_by_name(signal); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(py_signal_get_emitted_doc, + "signal_get_emmited() -> signal name string\n" + "\n" + "Get name of current signal\n" +); +static PyObject *py_signal_get_emitted(PyObject *self, PyObject *args) +{ + RET_AS_STRING_OR_NONE(signal_get_emitted()); +} + +PyDoc_STRVAR(py_signal_get_emitted_id_doc, + "signal_get_emmited_id() -> signal id int\n" + "\n" + "Get id of current signal\n" +); +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 + +PyDoc_STRVAR(py_themes_reload_doc, + "themes_reload() -> None\n" +); +static PyObject *py_themes_reload(PyObject *self, PyObject *args) +{ + themes_reload(); + Py_RETURN_NONE; +} + +PyDoc_STRVAR(py_current_theme_doc, + "current_theme() -> Theme object\n" +); +static PyObject *py_current_theme(PyObject *self, PyObject *args) +{ + if (current_theme) + return pytheme_new(current_theme); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(py_statusbar_items_redraw_doc, + "statusbar_items_redraw(name) -> None\n" +); +static PyObject *py_statusbar_items_redraw(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"name", NULL}; + char *name = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &name)) + return NULL; + + statusbar_items_redraw(name); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(py_statusbars_recreate_items_doc, + "statusbars_recreate_items() -> None" +); +static PyObject *py_statusbars_recreate_items(PyObject *self, PyObject *args) +{ + statusbars_recreate_items(); + + Py_RETURN_NONE; +} + +/* XXX: we can unregister any statusbar items, not just the ones from this script */ +PyDoc_STRVAR(py_statusbar_item_unregister_doc, + "statusbar_item_unregister(name) -> None" +); +static PyObject *py_statusbar_item_unregister(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"name", NULL}; + char *name = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &name)) + return NULL; + + pystatusbar_item_unregister(name); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(py_active_mainwin_doc, + "active_mainwin() -> MainWindow object\n" + "\n" + "return active main window\n" +); +static PyObject *py_active_mainwin(PyObject *self, PyObject *args) +{ + if (!active_mainwin) + Py_RETURN_NONE; + + return pymain_window_new(active_mainwin); +} + +PyDoc_STRVAR(py_mainwindows_doc, + "mainwindows() -> list of MainWindow objects\n" + "\n" + "return a list of mainwindows\n" +); +static PyObject *py_mainwindows(PyObject *self, PyObject *args) +{ + return py_irssi_objlist_new(mainwindows, 1, (InitFunc)pymain_window_new); +} + +/* expect a list of tuples [('str', 0), ...] */ +static GSList *py_register_conv(PyObject *list) +{ + int i; + GSList *ret = NULL; + + if (list == Py_None) + return NULL; + + if (!PyList_Check(list)) + { + PyErr_Format(PyExc_TypeError, "expect a list of tuples of string and int"); + return NULL; + } + + for (i = 0; i < PyList_Size(list); i++) + { + char *key; + int val; + PyObject *tup = PyList_GET_ITEM(list, i); + + if (!PyTuple_Check(tup) || !PyArg_ParseTuple(tup, "si", &key, &val)) + { + if (!PyErr_Occurred() || PyErr_ExceptionMatches(PyExc_TypeError)) + { + PyErr_Clear(); + PyErr_SetString(PyExc_TypeError, "expect a list of tuples of string and int"); + } + + /* return the list so far; caller will clear it on error */ + break; + } + + ret = g_slist_append(ret, g_strdup(key)); + ret = g_slist_append(ret, GINT_TO_POINTER(val)); + } + + return ret; +} + +PyDoc_STRVAR(py_server_redirect_register_doc, + "server_redirect_register(command, stop, start=None, opt=None, remote=False, timeout=0) -> None\n" + "\n" + "Register new redirection command. By default irssi has already\n" + "registered at least: whois, whowas, who, list, ison, userhost, ping,\n" + "\"mode channel\" (/MODE #channel), \"mode b\" (/MODE #channel b), \"mode e\"\n" + "and \"mode I\".\n" + "\n" + "`command' specifies the name of the command to register, it doesn't\n" + "have to be a real command name, but something you just specify to\n" + "Server.redirect_event() when using this redirection.\n" + "\n" + "`start', `stop', `opt' - lists of ('event', argpos) tuples.\n" + "List of events that start and stop this redirection.\n" + "Start event list may be empty, but there must be at least one\n" + "stop event. Optional events are checked only if they are received\n" + "immediately after one of the stop-events. `argpos' specifies the\n" + "word number in event string which is compared to wanted argument,\n" + "-1 = don't compare, TRUE always.\n" + "\n" + "`remote' specifies if the command is by default a remote command\n" + "(eg. sent to another server). Server.redirect_event() may override this.\n" + "\n" + "`timeout' - If remote is TRUE, specifies how many seconds to wait for\n" + "reply before aborting.\n" + "\n" + "Example (already done by irssi):\n" + "\n" + "server_redirect_register('mode channel',\n" + " start = None,\n" + " stop = [ \n" + " ('event 324', 1), # MODE-reply\n" + " ('event 403', 1), # no such channel\n" + " ('event 442', 1), # \"you're not on that channel\"\n" + " ('event 479', 1) # \"Cannot join channel (illegal name)\"\n" + " ], \n" + " opt = [ \n" + " ('event 329', 1) # Channel create time\n" + " ] \n" + ")\n" +); +static PyObject *py_server_redirect_register(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"command", "stop", "start", "opt", "remote", "timeout", NULL}; + char *command = ""; + int remote = 0; + int timeout = 0; + PyObject *start = NULL; + PyObject *stop = NULL; + PyObject *opt = NULL; + GSList *node; + GSList *gstart = NULL; + GSList *gstop = NULL; + GSList *gopt = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "sO|OOii", kwlist, + &command, &stop, &start, &opt, &remote, &timeout)) + return NULL; + + gstart = py_register_conv(start); + if (PyErr_Occurred()) + goto error; + + gstop = py_register_conv(stop); + if (!gstop || PyErr_Occurred()) + { + if (!PyErr_Occurred()) + PyErr_SetString(PyExc_TypeError, "stop list must be provided"); + + goto error; + } + + gopt = py_register_conv(opt); + if (PyErr_Occurred()) + goto error; + + server_redirect_register_list(command, remote, timeout, gstart, gstop, gopt); + + Py_RETURN_NONE; + +error: + /* clean up all lists */ + for (node = gstart; node; node = node->next->next) + g_free(node->data); + for (node = gstop; node; node = node->next->next) + g_free(node->data); + for (node = gopt; node; node = node->next->next) + g_free(node->data); + + g_slist_free(gstart); + g_slist_free(gstop); + g_slist_free(gopt); + + return NULL; + +} + +PyDoc_STRVAR(py_command_runsub_doc, + "command_runsub(cmd, data, server=None, item=None) -> None\n" + "\n" + "Run subcommands for `cmd'. First word in `data' is parsed as\n" + "subcommand. `server' is Server object for current\n" + "WindowItem `item'.\n" + "\n" + "Call command_runsub in handler function for `cmd' and bind\n" + "with command_bind(\"`cmd' `subcmd'\", subcmdfunc[, category]);\n" +); +static PyObject *py_command_runsub(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"cmd", "data", "server", "item", NULL}; + char *cmd = ""; + char *data = ""; + PyObject *pserver = Py_None; + PyObject *pitem = Py_None; + SERVER_REC *server = NULL; + WI_ITEM_REC *item = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss|OO", kwlist, + &cmd, &data, &pserver, &pitem)) + return NULL; + + if (pserver != Py_None && !pyserver_check(pserver)) + return PyErr_Format(PyExc_TypeError, "server must be a server"); + + if (pitem != Py_None && !pywindow_item_check(pitem)) + return PyErr_Format(PyExc_TypeError, "item must be a window item"); + + if (pserver != Py_None) + server = DATA(pserver); + + if (pitem != Py_None) + item = DATA(pitem); + + command_runsub(cmd, data, server, item); + + Py_RETURN_NONE; +} + +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}, + {"chatnet_find", (PyCFunction)py_chatnet_find, METH_VARARGS | METH_KEYWORDS, + py_chatnet_find_doc}, + {"chatnets", (PyCFunction)py_chatnets, METH_NOARGS, + py_chatnets_doc}, + {"reconnects", (PyCFunction)py_reconnects, METH_NOARGS, + py_reconnects_doc}, + {"servers", (PyCFunction)py_servers, METH_NOARGS, + py_servers_doc}, + {"windows", (PyCFunction)py_windows, METH_NOARGS, + py_windows_doc}, + {"active_win", (PyCFunction)py_active_win, METH_NOARGS, + py_active_win_doc}, + {"active_server", (PyCFunction)py_active_server, METH_NOARGS, + py_active_server_doc}, + {"window_find_name", (PyCFunction)py_window_find_name, METH_VARARGS | METH_KEYWORDS, + py_window_find_name_doc}, + {"window_find_refnum", (PyCFunction)py_window_find_refnum, METH_VARARGS | METH_KEYWORDS, + py_window_find_refnum_doc}, + {"window_refnum_prev", (PyCFunction)py_window_refnum_prev, METH_VARARGS | METH_KEYWORDS, + py_window_refnum_prev_doc}, + {"window_refnum_next", (PyCFunction)py_window_refnum_next, METH_VARARGS | METH_KEYWORDS, + py_window_refnum_next_doc}, + {"windows_refnum_last", (PyCFunction)py_windows_refnum_last, METH_NOARGS, + py_windows_refnum_last_doc}, + {"window_find_level", (PyCFunction)py_window_find_level, METH_VARARGS | METH_KEYWORDS, + py_window_find_level_doc}, + {"window_find_item", (PyCFunction)py_window_find_item, METH_VARARGS | METH_KEYWORDS, + py_window_find_item_doc}, + {"window_find_closest", (PyCFunction)py_window_find_closest, METH_VARARGS | METH_KEYWORDS, + py_window_find_closest_doc}, + {"window_item_find", (PyCFunction)py_window_item_find, METH_VARARGS | METH_KEYWORDS, + py_window_item_find_doc}, + {"window_create", (PyCFunction)py_window_create, METH_VARARGS | METH_KEYWORDS, + py_window_create_doc}, + {"server_find_tag", (PyCFunction)py_server_find_tag, METH_VARARGS | METH_KEYWORDS, + py_server_find_tag_doc}, + {"server_find_chatnet", (PyCFunction)py_server_find_chatnet, METH_VARARGS | METH_KEYWORDS, + py_server_find_chatnet_doc}, + {"command", (PyCFunction)PY_command, METH_VARARGS | METH_KEYWORDS, + PY_command_doc}, + {"channels", (PyCFunction)py_channels, METH_NOARGS, + py_channels_doc}, + {"channel_find", (PyCFunction)py_channel_find, METH_VARARGS | METH_KEYWORDS, + py_channel_find_doc}, + {"query_find", (PyCFunction)py_query_find, METH_VARARGS | METH_KEYWORDS, + py_query_find_doc}, + {"queries", (PyCFunction)py_queries, METH_NOARGS, + py_queries_doc}, + {"mask_match", (PyCFunction)py_mask_match, METH_VARARGS | METH_KEYWORDS, + py_mask_match_doc}, + {"mask_match_address", (PyCFunction)py_mask_match_address, METH_VARARGS | METH_KEYWORDS, + py_mask_match_address_doc}, + {"masks_match", (PyCFunction)py_masks_match, METH_VARARGS | METH_KEYWORDS, + py_masks_match_doc}, + {"rawlog_set_size", (PyCFunction)py_rawlog_set_size, METH_VARARGS | METH_KEYWORDS, + py_rawlog_set_size_doc}, + {"logs", (PyCFunction)py_logs, METH_NOARGS, + py_logs_doc}, + {"log_find", (PyCFunction)py_log_find, METH_VARARGS | METH_KEYWORDS, + py_log_find_doc}, + {"ignores", (PyCFunction)py_ignores, METH_NOARGS, + py_ignores_doc}, + {"ignore_check", (PyCFunction)py_ignore_check, METH_VARARGS | METH_KEYWORDS, + py_ignore_check_doc}, + {"dccs", (PyCFunction)py_dccs, METH_NOARGS, + py_dccs_doc}, + {"dcc_register_type", (PyCFunction)py_dcc_register_type, METH_VARARGS | METH_KEYWORDS, + py_dcc_register_type_doc}, + {"dcc_unregister_type", (PyCFunction)py_dcc_unregister_type, METH_VARARGS | METH_KEYWORDS, + py_dcc_unregister_type_doc}, + {"dcc_find_request_latest", (PyCFunction)py_dcc_find_request_latest, METH_VARARGS | METH_KEYWORDS, + py_dcc_find_request_latest_doc}, + {"dcc_find_request", (PyCFunction)py_dcc_find_request, METH_VARARGS | METH_KEYWORDS, + py_dcc_find_request_doc}, + {"dcc_chat_find_id", (PyCFunction)py_dcc_chat_find_id, METH_VARARGS | METH_KEYWORDS, + py_dcc_chat_find_id_doc}, + {"dcc_str2type", (PyCFunction)py_dcc_str2type, METH_VARARGS | METH_KEYWORDS, + py_dcc_str2type_doc}, + {"dcc_type2str", (PyCFunction)py_dcc_type2str, METH_VARARGS | METH_KEYWORDS, + py_dcc_type2str_doc}, + {"dcc_get_download_path", (PyCFunction)py_dcc_get_download_path, METH_VARARGS | METH_KEYWORDS, + py_dcc_get_download_path_doc}, + {"notifies", (PyCFunction)py_notifies, METH_NOARGS, + py_notifies_doc}, + {"notifylist_add", (PyCFunction)py_notifylist_add, METH_VARARGS | METH_KEYWORDS, + py_notifylist_add_doc}, + {"notifylist_remove", (PyCFunction)py_notifylist_remove, METH_VARARGS | METH_KEYWORDS, + py_notifylist_remove_doc}, + {"notifylist_ison", (PyCFunction)py_notifylist_ison, METH_VARARGS | METH_KEYWORDS, + 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}, + {"signal_emit", (PyCFunction)py_signal_emit, METH_VARARGS, + py_signal_emit_doc}, + {"signal_stop", (PyCFunction)py_signal_stop, METH_NOARGS, + py_signal_stop_doc}, + {"signal_stop_by_name", (PyCFunction)py_signal_stop_by_name, METH_VARARGS | METH_KEYWORDS, + py_signal_stop_by_name_doc}, + {"signal_get_emitted", (PyCFunction)py_signal_get_emitted, METH_NOARGS, + py_signal_get_emitted_doc}, + {"signal_get_emitted_id", (PyCFunction)py_signal_get_emitted_id, METH_NOARGS, + 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},*/ + {"themes_reload", (PyCFunction)py_themes_reload, METH_NOARGS, + py_themes_reload_doc}, + {"current_theme", (PyCFunction)py_current_theme, METH_NOARGS, + py_current_theme_doc}, + {"statusbar_items_redraw", (PyCFunction)py_statusbar_items_redraw, METH_VARARGS | METH_KEYWORDS, + py_statusbar_items_redraw_doc}, + {"statusbars_recreate_items", (PyCFunction)py_statusbars_recreate_items, METH_NOARGS, + py_statusbars_recreate_items_doc}, + {"statusbar_item_unregister", (PyCFunction)py_statusbar_item_unregister, METH_VARARGS | METH_KEYWORDS, + py_statusbar_item_unregister_doc}, + {"active_mainwin", (PyCFunction)py_active_mainwin, METH_NOARGS, + py_active_mainwin_doc}, + {"mainwindows", (PyCFunction)py_mainwindows, METH_NOARGS, + py_mainwindows_doc}, + {"server_redirect_register", (PyCFunction)py_server_redirect_register, METH_VARARGS | METH_KEYWORDS, + py_server_redirect_register_doc}, + {"command_runsub", (PyCFunction)py_command_runsub, METH_VARARGS | METH_KEYWORDS, + py_command_runsub_doc}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + +int pymodule_init(void) +{ + g_return_val_if_fail(py_module == NULL, 0); + + py_module = Py_InitModule("_irssi", ModuleMethods); + if (!py_module) + return 0; + + return 1; +} + +void pymodule_deinit(void) +{ + g_return_if_fail(py_module != NULL); + + Py_DECREF(py_module); + py_module = NULL; +} diff --git a/src/pymodule.h b/src/pymodule.h new file mode 100644 index 0000000..242eecf --- /dev/null +++ b/src/pymodule.h @@ -0,0 +1,11 @@ +#ifndef _PY_MODULE_H_ +#define _PY_MODULE_H_ + +#include <Python.h> + +/* This is global so that type objects and such can be easily attached */ +extern PyObject *py_module; +int pymodule_init(void); +void pymodule_deinit(void); + +#endif diff --git a/src/pysigmap.h b/src/pysigmap.h new file mode 100644 index 0000000..c34fbf7 --- /dev/null +++ b/src/pysigmap.h @@ -0,0 +1,188 @@ +/* Include in your C module */ +static PY_SIGNAL_SPEC_REC py_sigmap[] = { + {"gui exit", "", 0, 0, 0}, + {"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}, + {"command ", "sSW", 0, 0, 1}, + {"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", "ls", 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 changed", "", 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}, + {"ctcp msg ", "Sssss", 0, 0, 1}, + {"default ctcp msg", "Sssss", 0, 0, 0}, + {"ctcp reply", "Sssss", 0, 0, 0}, + {"ctcp reply ", "Sssss", 0, 0, 1}, + {"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}, + {"event ", "Ssss", 0, 0, 1}, + {"default event", "Ssss", 0, 0, 0}, + {"whois default event", "Ssss", 0, 0, 0}, + {"server incoming", "Ss", 0, 0, 0}, + {"redir ", "Ssss", 0, 0, 1}, + {"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", "SS", 0, 0, 0}, + {"netsplit server remove", "SS", 0, 0, 0}, + {"netsplit new", "N", 0, 0, 0}, + {"netsplit remove", "N", 0, 0, 0}, + {"dcc ctcp ", "sd", 0, 0, 1}, + {"default dcc ctcp", "sd", 0, 0, 0}, + {"dcc unknown ctcp", "sss", 0, 0, 0}, + {"dcc reply ", "sd", 0, 0, 1}, + {"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", "?wssi", 0, 0, 0}, + {"irssi init read settings", "", 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}, + {"beep", "", 0, 0, 0}, + {NULL} +}; + +#define py_sigmap_len() (sizeof(py_sigmap) / sizeof(py_sigmap[0]) - 1) diff --git a/src/pysignals.c b/src/pysignals.c new file mode 100644 index 0000000..95718aa --- /dev/null +++ b/src/pysignals.c @@ -0,0 +1,805 @@ +#include <Python.h> +#include "pyirssi.h" +#include "pysignals.h" +#include "factory.h" + +/* 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. Each PY_SIGNAL_REC stores data + * about a handler for a signal. + * + * 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 + * 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 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 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, 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 +{ + char *name; + char *arglist; + int refcount; + int dynamic; + int is_var; /* is this entry a prefix for a variable signal? */ +} 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; +static GTree *py_sigtree = 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_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) +{ + 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; +} + +int pysignals_command_bind_list(GSList **list, const char *command, + PyObject *func, const char *category, int priority) +{ + PY_SIGNAL_REC *rec = pysignals_command_bind(command, func, category, priority); + if (!rec) + return 0; + + *list = g_slist_append(*list, rec); + return 1; +} + +/* return NULL if signal is invalid */ +PY_SIGNAL_REC *pysignals_signal_add(const char *signal, PyObject *func, int priority) +{ + PY_SIGNAL_REC *rec = py_signal_rec_new(signal, func, NULL); + + if (rec == NULL) + return NULL; + + signal_add_full(MODULE_NAME, priority, SIGNAME(rec), + (SIGNAL_FUNC)py_sig_proxy, rec); + + return rec; +} + +int pysignals_signal_add_list(GSList **list, const char *signal, + PyObject *func, int priority) +{ + PY_SIGNAL_REC *rec = pysignals_signal_add(signal, func, priority); + if (!rec) + return 0; + + *list = g_slist_append(*list, rec); + return 1; +} + +void pysignals_command_unbind(PY_SIGNAL_REC *rec) +{ + g_return_if_fail(rec->is_signal == FALSE); + g_return_if_fail(rec->command != 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->is_signal == TRUE); + + signal_remove_full(SIGNAME(rec), (SIGNAL_FUNC)py_sig_proxy, rec); + py_signal_rec_destroy(rec); +} + +void pysignals_remove_generic(PY_SIGNAL_REC *rec) +{ + if (rec->is_signal) + pysignals_signal_remove(rec); + else + pysignals_command_unbind(rec); +} + +/* returns 1 when found and removed successfully */ +int pysignals_remove_search(GSList **siglist, const char *name, + PyObject *func, PSG_TYPE type) +{ + GSList *node; + + for (node = *siglist; node != NULL; node = node->next) + { + PY_SIGNAL_REC *sig = node->data; + + if ((sig->is_signal && type == PSG_COMMAND) || + (!sig->is_signal && type == PSG_SIGNAL)) + continue; + + if ((strcmp(SIGNAME(sig), name) == 0) && + (func == NULL || func == sig->handler)) + { + pysignals_remove_generic(sig); + *siglist = g_slist_delete_link(*siglist, node); + + /* deleting node won't harm iteration because it quits here */ + return 1; + } + } + + return 0; +} + +void pysignals_remove_list(GSList *siglist) +{ + GSList *node = siglist; + + for (node = siglist; node != NULL; node = node->next) + pysignals_remove_generic(node->data); +} + +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) + { + 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; + } + } + + return list; +} + +/* irssi obj -> PyObject */ +static PyObject *py_i2py(char code, void *iobj) +{ + if (iobj == NULL) + Py_RETURN_NONE; + + switch (code) + { + 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); + + case 'c': + case 'S': + case 'C': + case 'q': + case 'n': + case 'W': + return py_irssi_chat_new(iobj, 1); + + 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); + } + + return PyErr_Format(PyExc_TypeError, "unknown code %c", code); +} + +/* PyObject -> irssi obj*/ +static void *py_py2i(char code, PyObject *pobj, int arg, const char *signal) +{ + char *type; + + if (pobj == Py_None) + return NULL; + + switch (code) + { + /* XXX: string doesn't persist */ + case 's': + type = "str"; + if (PyString_Check(pobj)) return PyString_AsString(pobj); + break; + case 'i': + type = "int"; + if (PyInt_Check(pobj)) return (void*)PyInt_AsLong(pobj); + break; + + case 'L': /* list of nicks */ + /*FIXME*/ + return NULL; + + case 'c': + type = "Chatnet"; + if (pychatnet_check(pobj)) return DATA(pobj); + break; + case 'S': + type = "Server"; + if (pyserver_check(pobj)) return DATA(pobj); + break; + case 'C': + type = "Channel"; + if (pychannel_check(pobj)) return DATA(pobj); + break; + case 'q': + type = "Query"; + if (pyquery_check(pobj)) return DATA(pobj); + break; + case 'n': + type = "Nick"; + if (pynick_check(pobj)) return DATA(pobj); + break; + case 'W': + type = "WindowItem"; + if (pywindow_item_check(pobj)) return DATA(pobj); + break; + + case 'd': + type = "DCC"; + if (pydcc_check(pobj)) return DATA(pobj); + break; + + case 'r': + type = "Reconnect"; + if (pyreconnect_check(pobj)) return DATA(pobj); + break; + case 'o': + type = "Command"; + if (pycommand_check(pobj)) return DATA(pobj); + break; + case 'l': + type = "Log"; + if (pylog_check(pobj)) return DATA(pobj); + break; + case 'a': + type = "Rawlog"; + if (pyrawlog_check(pobj)) return DATA(pobj); + break; + case 'g': + type = "Ignore"; + if (pyignore_check(pobj)) return DATA(pobj); + break; + case 'b': + type = "Ban"; + if (pyban_check(pobj)) return DATA(pobj); + break; + case 'N': + type = "Netsplit"; + if (pynetsplit_check(pobj)) return DATA(pobj); + break; + case 'e': + type = "NetsplitServer"; + if (pynetsplit_server_check(pobj)) return DATA(pobj); + break; + case 'O': + type = "Notifylist"; + if (pynotifylist_check(pobj)) return DATA(pobj); + break; + case 'p': + type = "Process"; + if (pyprocess_check(pobj)) return DATA(pobj); + break; + case 't': + type = "TextDest"; + if (pytextdest_check(pobj)) return DATA(pobj); + break; + case 'w': + type = "Window"; + if (pywindow_check(pobj)) return DATA(pobj); + break; + default: + PyErr_Format(PyExc_TypeError, "don't know type code %c", code); + return NULL; + } + + 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; +} + +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++) + { + 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); + } + + 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++) + { + PyObject *arg = py_i2py(arglist[i], args[i]); + if (!arg) + goto error; + + PyTuple_SET_ITEM(argtup, i, arg); + } + + ret = PyObject_CallObject(rec->handler, argtup); + if (!ret) + goto error; + + /*XXX: IN/OUT arg handling not well tested */ + for (i = 0, j = 0; i < arglen; i++) + { + 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(); +} + +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); +} + +static int py_convert_args(void **args, PyObject *argtup, const char *signal) +{ + char *arglist; + PY_SIGNAL_SPEC_REC *spec; + int i; + int maxargs; + + spec = py_signal_lookup(signal); + if (!spec) + { + PyErr_Format(PyExc_KeyError, "signal not found"); + 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 < PyTuple_Size(argtup); i++) + { + args[i] = py_py2i(arglist[i], + PyTuple_GET_ITEM(argtup, i), + i+1, signal); + + if (PyErr_Occurred()) /* XXX: any cleanup needed? */ + return -1; + } + + 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]); + + return 1; +} + +/* 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 = py_signal_lookup(signal); + if (!spec) + return NULL; + + rec = g_new0(PY_SIGNAL_REC, 1); + rec->signal = spec; + rec->handler = func; + Py_INCREF(func); + + if (command) + { + rec->is_signal = FALSE; + rec->command = g_strdup(command); + } + else + { + rec->is_signal = TRUE; + /* handle variable signal. requested signal will be longer than spec->name, ie + * signal = "var signal POOOM", spec->name = "var signal " + */ + if (strcmp(signal, spec->name) != 0) + rec->command = g_strdup(signal); + } + + 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) +{ + if (sig->is_var) + g_tree_insert(py_sigtree, sig->name, sig); + else + g_hash_table_insert(py_sighash, sig->name, sig); +} + +static void py_signal_remove(PY_SIGNAL_SPEC_REC *sig) +{ + int ret; + + if (sig->is_var) + g_tree_remove(py_sigtree, sig->name); + else + { + ret = g_hash_table_remove(py_sighash, sig->name); + g_return_if_fail(ret != FALSE); + } +} + +static int precmp(const char *spec, const char *test) +{ + while (*spec == *test++) + if (*spec++ == '\0') + return 0; + + /* Variable event prefix matches (spec must never be empty string)*/ + /* precmp("var event ", "var event POOOM") -> 0 */ + if (*spec == '\0' && *(spec-1) == ' ') + return 0; + + return *(const unsigned char *)(test - 1) - *(const unsigned char *)spec; +} + +static PY_SIGNAL_SPEC_REC *py_signal_lookup(const char *name) +{ + PY_SIGNAL_SPEC_REC *ret; + + /* First check the normal signals hash, then check the variable signal prefixes in the tree */ + ret = g_hash_table_lookup(py_sighash, name); + if (!ret) + ret = g_tree_search(py_sigtree, (GCompareFunc)precmp, name); + + return ret; +} + +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); + g_return_val_if_fail(sig->refcount > 1 || sig->dynamic, 0); + + sig->refcount--; + + if (sig->refcount == 0) + { + py_signal_remove(sig); + + /* freeing name also takes care of the key */ + g_free(sig->name); + g_free(sig->arglist); + g_free(sig); + + return 1; + } + + return 0; +} + +/* returns 0 when signal already exists, but with different args, or when + similar signal prefix is already present. */ +int pysignals_register(const char *name, const char *arglist) +{ + int len; + PY_SIGNAL_SPEC_REC *spec; + + len = strlen(name); + g_return_val_if_fail(len > 0, 0); + + spec = py_signal_lookup(name); + if (!spec) + { + spec = g_new0(PY_SIGNAL_SPEC_REC, 1); + spec->is_var = name[len-1] == ' '; /* trailing space means signal prefix */ + spec->dynamic = 1; + spec->refcount = 0; + spec->name = g_strdup(name); + spec->arglist = g_strdup(arglist); + + py_signal_add(spec); + } + else if (strcmp(spec->arglist, arglist) || strcmp(spec->name, name)) + 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 = py_signal_lookup(name); + if (!spec) + return 0; + + py_signal_unref(spec); + return 1; +} + +void pysignals_init(void) +{ + int i; + + g_return_if_fail(py_sighash == NULL); + g_return_if_fail(py_sigtree == NULL); + + py_sigtree = g_tree_new((GCompareFunc)strcmp); + py_sighash = g_hash_table_new(g_str_hash, g_str_equal); + + for (i = 0; i < py_sigmap_len(); i++) + { + py_sigmap[i].refcount = 1; + py_sigmap[i].dynamic = 0; + 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, FALSE); + g_return_val_if_fail(value->refcount == 1, FALSE); + + return FALSE; +} + +/* XXX: remember to remove all scripts before calling this deinit */ +void pysignals_deinit(void) +{ + g_return_if_fail(py_sighash != NULL); + g_return_if_fail(py_sigtree != NULL); + + g_tree_foreach(py_sigtree, (GTraverseFunc)py_check_sig, NULL); + g_hash_table_foreach_remove(py_sighash, (GHRFunc)py_check_sig, NULL); + + g_tree_destroy(py_sigtree); + g_hash_table_destroy(py_sighash); + py_sigtree = NULL; + py_sighash = NULL; +} diff --git a/src/pysignals.h b/src/pysignals.h new file mode 100644 index 0000000..bec72c9 --- /dev/null +++ b/src/pysignals.h @@ -0,0 +1,44 @@ +#ifndef _PYSIGNALS_H_ +#define _PYSIGNALS_H_ +#include <Python.h> + +/* forward */ +struct _PY_SIGNAL_SPEC_REC; + +typedef struct _PY_SIGNAL_REC +{ + struct _PY_SIGNAL_SPEC_REC *signal; + char *command; /* used for command and variable signal */ + PyObject *handler; + int is_signal; +} PY_SIGNAL_REC; + +typedef enum +{ + PSG_COMMAND, + PSG_SIGNAL, + PSG_ALL, +} PSG_TYPE; + +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); +int pysignals_command_bind_list(GSList **list, const char *command, + PyObject *func, const char *category, int priority); +int pysignals_signal_add_list(GSList **list, 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_remove_search(GSList **siglist, const char *name, + PyObject *func, PSG_TYPE type); +void pysignals_remove_list(GSList *siglist); +int pysignals_emit(const char *signal, PyObject *argtup); +int pysignals_continue(PyObject *argtup); +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/src/pysource.c b/src/pysource.c new file mode 100644 index 0000000..413ab2c --- /dev/null +++ b/src/pysource.c @@ -0,0 +1,130 @@ +#include <Python.h> +#include "pyirssi.h" +#include "pysource.h" + +typedef struct _PY_SOURCE_REC +{ + int tag; + GSList **tag_list; + int fd; + PyObject *func; + PyObject *data; +} PY_SOURCE_REC; + +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->tag_list = tag_list; + rec->fd = fd; + rec->func = func; + rec->data = 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_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_handle_ret(PyObject *ret) +{ + int res; + + if (!ret) + { + PyErr_Print(); + res = FALSE; + } + else + { + res = PyObject_IsTrue(ret); + Py_DECREF(ret); + } + + return res; +} + +static int py_timeout_proxy(PY_SOURCE_REC *rec) +{ + PyObject *ret; + + g_return_val_if_fail(rec != NULL, FALSE); + + if (rec->data) + ret = PyObject_CallFunction(rec->func, "O", rec->data); + else + ret = PyObject_CallFunction(rec->func, ""); + + return py_handle_ret(ret); +} + +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 + ret = PyObject_CallFunction(rec->func, "ii", rec->fd, condition); + + return py_handle_ret(ret); +} + +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_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_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_rec_new(list, fd, func, data); + channel = g_io_channel_unix_new(fd); + 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, GINT_TO_POINTER(rec->tag)); + + return rec->tag; +} diff --git a/src/pysource.h b/src/pysource.h new file mode 100644 index 0000000..78b55ec --- /dev/null +++ b/src/pysource.h @@ -0,0 +1,10 @@ +#ifndef _PYSOURCE_H_ +#define _PYSOURCE_H_ + +#include <Python.h> + +/* condition is G_INPUT_READ or G_INPUT_WRITE */ +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/src/pystatusbar.c b/src/pystatusbar.c new file mode 100644 index 0000000..8537718 --- /dev/null +++ b/src/pystatusbar.c @@ -0,0 +1,132 @@ +#include "pystatusbar.h" +#include "pyirssi.h" +#include "factory.h" + +typedef struct +{ + char *name; + PyObject *script; + PyObject *handler; +} PY_BAR_ITEM_REC; + +/* Map: item name -> bar item obj */ +static GHashTable *py_bar_items = NULL; + +static void py_add_bar_handler(const char *iname, PyObject *script, PyObject *handler) +{ + PY_BAR_ITEM_REC *sitem; + + sitem = g_new0(PY_BAR_ITEM_REC, 1); + sitem->name = g_strdup(iname); + sitem->script = script; + sitem->handler = handler; + Py_INCREF(script); + Py_INCREF(handler); + + g_hash_table_insert(py_bar_items, sitem->name, sitem); +} + +static void py_destroy_handler(PY_BAR_ITEM_REC *sitem) +{ + statusbar_item_unregister(sitem->name); + + g_free(sitem->name); /* destroy key */ + Py_DECREF(sitem->script); + Py_DECREF(sitem->handler); + g_free(sitem); +} + +static void py_statusbar_proxy_call(SBAR_ITEM_REC *item, int sizeonly, PY_BAR_ITEM_REC *sitem) +{ + PyObject *pybaritem; + PyObject *ret; + + g_return_if_fail(PyCallable_Check(sitem->handler)); + + pybaritem = pystatusbar_item_new(item); + if (!pybaritem) + { + PyErr_Print(); + pystatusbar_item_unregister(sitem->name); + } + + ret = PyObject_CallFunction(sitem->handler, "Oi", pybaritem, sizeonly); + if (!ret) + { + PyErr_Print(); + pystatusbar_item_unregister(sitem->name); + } + else + Py_DECREF(ret); +} + +static void py_statusbar_proxy(SBAR_ITEM_REC *item, int sizeonly) +{ + PY_BAR_ITEM_REC *sitem; + + sitem = g_hash_table_lookup(py_bar_items, item->config->name); + if (sitem) + py_statusbar_proxy_call(item, sizeonly, sitem); + else + { + statusbar_item_default_handler(item, sizeonly, NULL, "", TRUE); + g_critical("unknown handler for Python statusbar proxy: %s", item->config->name); + } +} + +void pystatusbar_item_register(PyObject *script, const char *sitem, + const char *value, PyObject *func) +{ + if (func) + { + g_return_if_fail(PyCallable_Check(func)); + py_add_bar_handler(sitem, script, func); + } + + statusbar_item_register(sitem, value, func? py_statusbar_proxy : NULL); +} + +/* remove selected status bar item handler */ +void pystatusbar_item_unregister(const char *iname) +{ + if (!g_hash_table_remove(py_bar_items, iname)) + statusbar_item_unregister(iname); +} + +/* remove all statusbar item handlers for script */ +/* XXX: Only status bar items registered with a handler are stored in the hash table. + * Items registered with only a value are not stored, so there is no way to unregister + * them when the script is unloaded. + */ +static int py_check_clean(char *key, PY_BAR_ITEM_REC *value, PyObject *script) +{ + if (value->script == script) + return 1; + + return 0; +} + +void pystatusbar_cleanup_script(PyObject *script) +{ + g_hash_table_foreach_remove(py_bar_items, (GHRFunc)py_check_clean, script); +} + +void pystatusbar_init(void) +{ + g_return_if_fail(py_bar_items == NULL); + + /* key is freed by destroy_handler */ + py_bar_items = g_hash_table_new_full(g_str_hash, g_str_equal, + NULL, (GDestroyNotify)py_destroy_handler); +} + +/* XXX: this must be called after cleaning up all the loaded scripts */ +void pystatusbar_deinit(void) +{ + g_return_if_fail(py_bar_items != NULL); + g_return_if_fail(g_hash_table_size(py_bar_items) == 0); + + g_hash_table_destroy(py_bar_items); + py_bar_items = NULL; +} + diff --git a/src/pystatusbar.h b/src/pystatusbar.h new file mode 100644 index 0000000..9749c9f --- /dev/null +++ b/src/pystatusbar.h @@ -0,0 +1,13 @@ +#ifndef _PYSTATUSBAR_H_ +#define _PYSTATUSBAR_H_ + +#include <Python.h> + +void pystatusbar_item_register(PyObject *script, const char *sitem, + const char *value, PyObject *func); +void pystatusbar_item_unregister(const char *iname); +void pystatusbar_cleanup_script(PyObject *script); +void pystatusbar_init(void); +void pystatusbar_deinit(void); + +#endif diff --git a/src/pythemes.c b/src/pythemes.c new file mode 100644 index 0000000..a3e8927 --- /dev/null +++ b/src/pythemes.c @@ -0,0 +1,287 @@ +#include <Python.h> +#include "pythemes.h" +#include "pyirssi.h" +#include "factory.h" +#include "pymodule.h" +#include "pyloader.h" + +static void py_get_mod(char *full, int fullsz, const char *script) +{ + g_snprintf(full, fullsz, "irssi_python/%s.py", script); +} + +/* Edited from Perl Themes.xs */ +int pythemes_printformat(TEXT_DEST_REC *dest, const char *name, const char *format, PyObject *argtup) +{ + char *arglist[MAX_FORMAT_PARAMS + 1]; + THEME_REC *theme; + char *str; + char script[256]; + int formatnum; + int i; + + py_get_mod(script, sizeof script, name); + + 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 *name, PyObject *list) +{ + char script[256]; + FORMAT_REC *formatrecs; + int i; + + py_get_mod(script, sizeof script, name); + + 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].def = 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 (!PyTuple_Check(item) || !PyArg_ParseTuple(item, "ss", &key, &value)) + { + if (!PyErr_Occurred() || 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 *name) +{ + char script[256]; + FORMAT_REC *formats; + + py_get_mod(script, sizeof script, name); + + formats = g_hash_table_lookup(default_formats, script); + if (!formats) + return; + + py_destroy_format_list(formats); + theme_unregister_module(script); +} + +/* XXX: test binding a PyCFunction to different sources. Not sure + if this is a good thing or not, but it seems to work */ +PyDoc_STRVAR(py_printformat_doc, + "for Server objects:\n" + "printformat(target, level, format, ...) -> None\n" + "\n" + "For all else:\n" + "printformat(level, format, ...) -> None\n" +); +static PyObject *py_printformat(PyObject *self, PyObject *all) +{ + int level; + char *format; + char *target; + PyObject *args = NULL, *varargs = NULL; + TEXT_DEST_REC dest; + char *script; + int formatstart; + + if (self && pyserver_check(self)) + formatstart = 3; + else + formatstart = 2; + + args = PySequence_GetSlice(all, 0, formatstart); + if (!args) + goto error; + + varargs = PySequence_GetSlice(all, formatstart, PyTuple_Size(all)); + if (!varargs) + goto error; + + if (self && pyserver_check(self)) + { + if (!PyArg_ParseTuple(args, "sis", &target, &level, &format)) + goto error; + } + else + { + 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; + } + + /* create the text dest depending on whether this function is called from + module level or as a method of one of the objects */ + if (self == NULL) /* module */ + format_create_dest(&dest, NULL, NULL, level, NULL); + else if (pyserver_check(self)) + format_create_dest(&dest, DATA(self), target, level, NULL); + else if (pywindow_check(self)) + format_create_dest(&dest, NULL, NULL, level, DATA(self)); + else if (pywindow_item_check(self)) + { + PyWindowItem *pywi = (PyWindowItem *)self; + format_create_dest(&dest, pywi->data->server, pywi->data->visible_name, 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; +} + +/* XXX: these funcs could be moved to pyutils.c */ +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/src/pythemes.h b/src/pythemes.h new file mode 100644 index 0000000..92af965 --- /dev/null +++ b/src/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 diff --git a/src/pyutils.c b/src/pyutils.c new file mode 100644 index 0000000..05e1f31 --- /dev/null +++ b/src/pyutils.c @@ -0,0 +1,81 @@ +#include <string.h> +#include "pyirssi.h" +#include "pyutils.h" +#include "settings.h" +#include "servers.h" + +/* copy paste from perl bindings */ +void py_command(const char *cmd, SERVER_REC *server, WI_ITEM_REC *item) +{ + const char *cmdchars; + char *sendcmd = (char *) cmd; + + if (*cmd == '\0') + return; + + cmdchars = settings_get_str("cmdchars"); + if (strchr(cmdchars, *cmd) == NULL) { + /* no command char - let's put it there.. */ + sendcmd = g_strdup_printf("%c%s", *cmdchars, cmd); + } + + signal_emit("send command", 3, sendcmd, server, item); + if (sendcmd != cmd) g_free(sendcmd); +} + +/* return the file extension for a file, or empty string + don't free result */ +char *file_get_ext(const char *file) +{ + const char *dot = NULL; + + while (*file) + { + if (*file == '.') + dot = file; + + file++; + } + + if (dot) + return (char *) dot + 1; + + return (char *) file; +} + +int file_has_ext(const char *file, const char *ext) +{ + const char *fext = file_get_ext(file); + + return !strcmp(fext, ext); +} + + +/* return whats in the braces -> /path/to/{filename}.py + result must be freed */ +char *file_get_filename(const char *path) +{ + const char *begin; + const char *end; + char *name; + size_t len; + + begin = strrchr(path, '/'); + if (!begin) + begin = path; + else + begin++; + + end = strrchr(begin, '.'); + if (end != NULL && end > begin) + len = end - begin; + else + len = strlen(begin); + + name = g_strnfill(len, 0); + + strncpy(name, begin, len); + + return name; +} + diff --git a/src/pyutils.h b/src/pyutils.h new file mode 100644 index 0000000..7b2ca7b --- /dev/null +++ b/src/pyutils.h @@ -0,0 +1,12 @@ +#ifndef _PYUTILS_H_ +#define _PYUTILS_H_ + +#include "servers.h" + +void py_command(const char *cmd, SERVER_REC *server, WI_ITEM_REC *item); +char *file_get_ext(const char *file); +int file_has_ext(const char *file, const char *ext); +char *file_get_filename(const char *path); + + +#endif diff --git a/src/sig2code.awk b/src/sig2code.awk new file mode 100644 index 0000000..d6bdb0d --- /dev/null +++ b/src/sig2code.awk @@ -0,0 +1,61 @@ +BEGIN { + FS = "[ \t]*->[ \t]*"; + + #read in codes + while (getline < "sig2code.txt") + { + sub(/^[ \t]*/, ""); + if (NF < 2) + continue; + + #print $1, $2 + sigmap[$2] = $1 + } + + close("sig2code.txt"); + + FS = "[ \t]*,[ \t]*"; + + print "/* Include in your C module */"; + print "static PY_SIGNAL_SPEC_REC py_sigmap[] = {"; +} + +function match_type(t) +{ + for (type in sigmap) + { + if (index(t, type) != 0) + return sigmap[type]; + } + + return "?"; +} + +$1 ~ /^[ \t]*"/ && $1 !~ /"script error"/ { + sub(/^[ \t]*/, ""); + + signal = $1 + if (signal ~ /.*<cmd>$/) + { + varsig = 1; + sub(/<cmd>/, "", signal); + } + else + varsig = 0; + + args = ""; + for (i = 2; i <= NF; i++) + { + args = args""match_type($i); + } + + printf(" {%s, \"%s\", 0, 0, %d},\n", signal, args, varsig); +} + +END { + print " {NULL}" ; + print "};"; + print ""; + print "#define py_sigmap_len() (sizeof(py_sigmap) / sizeof(py_sigmap[0]) - 1)"; +} + diff --git a/src/sig2code.txt b/src/sig2code.txt new file mode 100644 index 0000000..379e88e --- /dev/null +++ b/src/sig2code.txt @@ -0,0 +1,44 @@ + 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 + |