diff options
author | Sadrul Habib Chowdhury <sadrul@users.sourceforge.net> | 2010-04-03 04:05:40 -0400 |
---|---|---|
committer | Sadrul Habib Chowdhury <sadrul@users.sourceforge.net> | 2010-04-03 04:05:40 -0400 |
commit | df282d7e09534e2be982d134447b29b023fc0d1c (patch) | |
tree | 2c2db2074eaadec1986f72c978b52efc230c13d5 | |
parent | 3d5dc020e65450446f247ca7b4ea1c7fc5649aa7 (diff) | |
parent | c250a4aa1caf7bc955407e2846149632a84a1f46 (diff) | |
download | screen-df282d7e09534e2be982d134447b29b023fc0d1c.tar.gz |
Merge branch 'scripting-python' into scripting
Conflicts:
src/Makefile.in
-rw-r--r-- | src/Makefile.in | 8 | ||||
-rw-r--r-- | src/acconfig.h | 1 | ||||
-rw-r--r-- | src/acinclude.m4 | 210 | ||||
-rw-r--r-- | src/configure.in | 3 | ||||
-rw-r--r-- | src/python.c | 659 | ||||
-rw-r--r-- | src/script.c | 82 | ||||
-rw-r--r-- | src/script.h | 2 | ||||
-rw-r--r-- | src/scripts/callback.py | 18 | ||||
-rw-r--r-- | src/scripts/input.py | 9 | ||||
-rw-r--r-- | src/scripts/py.py | 13 |
10 files changed, 982 insertions, 23 deletions
diff --git a/src/Makefile.in b/src/Makefile.in index 58b961a..e6d094b 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -29,11 +29,11 @@ ETCSCREENRC = $(prefix)/etc/screenrc SCREENENCODINGS = $(datadir)/screen/utf8encodings CC = @CC@ -CFLAGS = @CFLAGS@ @LUA_CFLAGS@ +CFLAGS = @CFLAGS@ @LUA_CFLAGS@ @PYTHON_INCLUDES@ CPPFLAGS = @CPPFLAGS@ -DETCSCREENRC='"$(ETCSCREENRC)"' \ -DSCREENENCODINGS='"$(SCREENENCODINGS)"' LDFLAGS = @LDFLAGS@ -LIBS = @LIBS@ @LUA_LIBS@ +LIBS = @LIBS@ @LUA_LIBS@ @PYTHON_LIBS@ CPP=@CPP@ CPP_DEPEND=$(CC) -MM @@ -63,14 +63,14 @@ CFILES= screen.c ansi.c fileio.c mark.c misc.c resize.c socket.c \ kmapdef.c acls.c braille.c braille_tsi.c logfile.c layer.c \ sched.c teln.c nethack.c encoding.c canvas.c layout.c viewport.c \ list_display.c list_generic.c list_window.c \ - lua.c script.c + lua.c script.c python.c OFILES= screen.o ansi.o fileio.o mark.o misc.o resize.o socket.o \ search.o tty.o term.o window.o utmp.o loadav.o putenv.o help.o \ termcap.o input.o attacher.o pty.o process.o display.o comm.o \ kmapdef.o acls.o braille.o braille_tsi.o logfile.o layer.o \ list_generic.o list_display.o list_window.o \ sched.o teln.o nethack.o encoding.o canvas.o layout.o viewport.o \ - lua.o script.o + lua.o script.o python.o all: screen diff --git a/src/acconfig.h b/src/acconfig.h index 585de3d..8de2b8c 100644 --- a/src/acconfig.h +++ b/src/acconfig.h @@ -168,6 +168,7 @@ /*Include the binding you would like to use.*/ #ifdef SCRIPT #define LUA_BINDING +#define PY_BINDING #endif #undef BUILTIN_TELNET diff --git a/src/acinclude.m4 b/src/acinclude.m4 new file mode 100644 index 0000000..8cc86f7 --- /dev/null +++ b/src/acinclude.m4 @@ -0,0 +1,210 @@ +# Ripped from pygobject-2.0 +# +# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 +# Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# AM_PATH_PYTHON([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) + +# Adds support for distributing Python modules and packages. To +# install modules, copy them to $(pythondir), using the python_PYTHON +# automake variable. To install a package with the same name as the +# automake package, install to $(pkgpythondir), or use the +# pkgpython_PYTHON automake variable. + +# The variables $(pyexecdir) and $(pkgpyexecdir) are provided as +# locations to install python extension modules (shared libraries). +# Another macro is required to find the appropriate flags to compile +# extension modules. + +# If your package is configured with a different prefix to python, +# users will have to add the install directory to the PYTHONPATH +# environment variable, or create a .pth file (see the python +# documentation for details). + +# If the MINIMUM-VERSION argument is passed, AM_PATH_PYTHON will +# cause an error if the version of python installed on the system +# doesn't meet the requirement. MINIMUM-VERSION should consist of +# numbers and dots only. + +AC_DEFUN([AM_PATH_PYTHON], + [ + dnl Find a Python interpreter. Python versions prior to 1.5 are not + dnl supported because the default installation locations changed from + dnl $prefix/lib/site-python in 1.4 to $prefix/lib/python1.5/site-packages + dnl in 1.5. + m4_define([_AM_PYTHON_INTERPRETER_LIST], + [python2.5 python python2 python2.4 python2.3 python2.2 dnl +python2.1 python2.0 python1.6 python1.5]) + + m4_if([$1],[],[ + dnl No version check is needed. + # Find any Python interpreter. + if test -z "$PYTHON"; then + PYTHON=: + AC_PATH_PROGS([PYTHON], _AM_PYTHON_INTERPRETER_LIST) + fi + am_display_PYTHON=python + ], [ + dnl A version check is needed. + if test -n "$PYTHON"; then + # If the user set $PYTHON, use it and don't search something else. + AC_MSG_CHECKING([whether $PYTHON version >= $1]) + AM_PYTHON_CHECK_VERSION([$PYTHON], [$1], + [AC_MSG_RESULT(yes)], + [AC_MSG_ERROR(too old)]) + am_display_PYTHON=$PYTHON + else + # Otherwise, try each interpreter until we find one that satisfies + # VERSION. + AC_CACHE_CHECK([for a Python interpreter with version >= $1], + [am_cv_pathless_PYTHON],[ + for am_cv_pathless_PYTHON in _AM_PYTHON_INTERPRETER_LIST none; do + test "$am_cv_pathless_PYTHON" = none && break + AM_PYTHON_CHECK_VERSION([$am_cv_pathless_PYTHON], [$1], [break]) + done]) + # Set $PYTHON to the absolute path of $am_cv_pathless_PYTHON. + if test "$am_cv_pathless_PYTHON" = none; then + PYTHON=: + else + AC_PATH_PROG([PYTHON], [$am_cv_pathless_PYTHON]) + fi + am_display_PYTHON=$am_cv_pathless_PYTHON + fi + ]) + + if test "$PYTHON" = :; then + dnl Run any user-specified action, or abort. + m4_default([$3], [AC_MSG_ERROR([no suitable Python interpreter found])]) + else + + dnl Query Python for its version number. Getting [:3] seems to be + dnl the best way to do this; it's what "site.py" does in the standard + dnl library. + + AC_CACHE_CHECK([for $am_display_PYTHON version], [am_cv_python_version], + [am_cv_python_version=`$PYTHON -c "import sys; print sys.version[[:3]]"`]) + AC_SUBST([PYTHON_VERSION], [$am_cv_python_version]) + + dnl Use the values of $prefix and $exec_prefix for the corresponding + dnl values of PYTHON_PREFIX and PYTHON_EXEC_PREFIX. These are made + dnl distinct variables so they can be overridden if need be. However, + dnl general consensus is that you shouldn't need this ability. + + AC_SUBST([PYTHON_PREFIX], ['${prefix}']) + AC_SUBST([PYTHON_EXEC_PREFIX], ['${exec_prefix}']) + + dnl At times (like when building shared libraries) you may want + dnl to know which OS platform Python thinks this is. + + AC_CACHE_CHECK([for $am_display_PYTHON platform], [am_cv_python_platform], + [am_cv_python_platform=`$PYTHON -c "import sys; print sys.platform"`]) + AC_SUBST([PYTHON_PLATFORM], [$am_cv_python_platform]) + + + dnl Set up 4 directories: + + dnl pythondir -- where to install python scripts. This is the + dnl site-packages directory, not the python standard library + dnl directory like in previous automake betas. This behavior + dnl is more consistent with lispdir.m4 for example. + dnl Query distutils for this directory. distutils does not exist in + dnl Python 1.5, so we fall back to the hardcoded directory if it + dnl doesn't work. + AC_CACHE_CHECK([for $am_display_PYTHON script directory], + [am_cv_python_pythondir], + [am_cv_python_pythondir=`$PYTHON -c "from distutils import sysconfig; print sysconfig.get_python_lib(0,0,prefix='$PYTHON_PREFIX')" 2>/dev/null || + echo "$PYTHON_PREFIX/lib/python$PYTHON_VERSION/site-packages"`]) + AC_SUBST([pythondir], [$am_cv_python_pythondir]) + + dnl pkgpythondir -- $PACKAGE directory under pythondir. Was + dnl PYTHON_SITE_PACKAGE in previous betas, but this naming is + dnl more consistent with the rest of automake. + + AC_SUBST([pkgpythondir], [\${pythondir}/$PACKAGE]) + + dnl pyexecdir -- directory for installing python extension modules + dnl (shared libraries) + dnl Query distutils for this directory. distutils does not exist in + dnl Python 1.5, so we fall back to the hardcoded directory if it + dnl doesn't work. + AC_CACHE_CHECK([for $am_display_PYTHON extension module directory], + [am_cv_python_pyexecdir], + [am_cv_python_pyexecdir=`$PYTHON -c "from distutils import sysconfig; print sysconfig.get_python_lib(1,0,prefix='$PYTHON_EXEC_PREFIX')" 2>/dev/null || + echo "${PYTHON_EXEC_PREFIX}/lib/python${PYTHON_VERSION}/site-packages"`]) + AC_SUBST([pyexecdir], [$am_cv_python_pyexecdir]) + + dnl pkgpyexecdir -- $(pyexecdir)/$(PACKAGE) + + AC_SUBST([pkgpyexecdir], [\${pyexecdir}/$PACKAGE]) + + dnl Run any user-specified action. + $2 + fi + +]) + + +# AM_PYTHON_CHECK_VERSION(PROG, VERSION, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) +# --------------------------------------------------------------------------- +# Run ACTION-IF-TRUE if the Python interpreter PROG has version >= VERSION. +# Run ACTION-IF-FALSE otherwise. +# This test uses sys.hexversion instead of the string equivalent (first +# word of sys.version), in order to cope with versions such as 2.2c1. +# hexversion has been introduced in Python 1.5.2; it's probably not +# worth to support older versions (1.5.1 was released on October 31, 1998). +AC_DEFUN([AM_PYTHON_CHECK_VERSION], + [prog="import sys, string +# split strings by '.' and convert to numeric. Append some zeros +# because we need at least 4 digits for the hex conversion. +minver = map(int, string.split('$2', '.')) + [[0, 0, 0]] +minverhex = 0 +for i in xrange(0, 4): minverhex = (minverhex << 8) + minver[[i]] +sys.exit(sys.hexversion < minverhex)" + AS_IF([AM_RUN_LOG([$1 -c "$prog"])], [$3], [$4])]) + +dnl a macro to check for ability to create python extensions +dnl AM_CHECK_PYTHON_HEADERS([ACTION-IF-POSSIBLE], [ACTION-IF-NOT-POSSIBLE]) +dnl function also defines PYTHON_INCLUDES +AC_DEFUN([AM_CHECK_PYTHON_HEADERS], +[AC_REQUIRE([AM_PATH_PYTHON]) +AC_MSG_CHECKING(for headers required to compile python extensions) +dnl deduce PYTHON_INCLUDES +py_prefix=`$PYTHON -c "import sys; print sys.prefix"` +py_exec_prefix=`$PYTHON -c "import sys; print sys.exec_prefix"` +if test -x "$PYTHON-config"; then +PYTHON_INCLUDES=`$PYTHON-config --includes 2>/dev/null` +PYTHON_LIBS=`$PYTHON-config --libs 2>/dev/null` +else +PYTHON_INCLUDES="-I${py_prefix}/include/python${PYTHON_VERSION}" +if test "$py_prefix" != "$py_exec_prefix"; then + PYTHON_INCLUDES="$PYTHON_INCLUDES -I${py_exec_prefix}/include/python${PYTHON_VERSION}" +fi +fi +AC_SUBST(PYTHON_INCLUDES) +AC_SUBST(PYTHON_LIBS) +dnl check if the headers exist: +save_CPPFLAGS="$CPPFLAGS" +CPPFLAGS="$CPPFLAGS $PYTHON_INCLUDES" +AC_TRY_CPP([#include <Python.h>],dnl +[AC_MSG_RESULT(found) +$1],dnl +[AC_MSG_RESULT(not found) +$2]) +CPPFLAGS="$save_CPPFLAGS" +]) + diff --git a/src/configure.in b/src/configure.in index 4cf9376..cb6f541 100644 --- a/src/configure.in +++ b/src/configure.in @@ -1301,6 +1301,9 @@ PKG_CHECK_MODULES(LUA, [lua5.1], , [ *** (http://git.savannah.gnu.org/gitweb/?p=screen.git;a=summary). ])]) +AM_CHECK_PYTHON_HEADERS(, [AC_MSG_ERROR(could not find Python headers.)]) +AC_SUBST(PYTHON_INCLUDES) + AC_SUBST(LUA_CFLAGS) AC_SUBST(LUA_LIBS) diff --git a/src/python.c b/src/python.c new file mode 100644 index 0000000..bfe6f71 --- /dev/null +++ b/src/python.c @@ -0,0 +1,659 @@ +/* Python scripting support + * + * Copyright (c) 2009 Sadrul Habib Chowdhury (sadrul@users.sf.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING); if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + **************************************************************** + */ +#include <sys/types.h> + +#include "config.h" +#include "screen.h" +#include "script.h" +#include <sys/stat.h> +#include <unistd.h> + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "extern.h" +#include "logfile.h" + +#include <Python.h> +#include <structmember.h> + +#define RETURN_NONE do { Py_INCREF(Py_None); return Py_None; } while (0) + +extern struct win *windows; +extern struct display *display, *displays; +extern struct layer *flayer; + +static PyObject * SPy_Get(PyObject *obj, void *closure); +static int SPy_Set(PyObject *obj, PyObject *value, void *closure); +static int PyDispatch(void *handler, const char *params, va_list va); +static PyObject * register_event_hook(PyObject *self, PyObject *args, PyObject *kw, void *object); + +typedef struct +{ + PyObject *callback; + struct listener *listener; +} SPyCallback; + +typedef struct +{ + char *name; + char *doc; + + int type; + size_t offset1; + size_t offset2; + PyObject * (*conv)(void *); + int (*setter)(void *, PyObject *); +} SPyClosure; + + +#define compare_callback NULL +#define repr_callback NULL + +#define REGISTER_TYPE(type, Type, closures, methods) \ +static int \ +register_##type(PyObject *module) \ +{ \ + static PyGetSetDef getsets[sizeof(closures)]; \ + int i, count = sizeof(closures); \ + for (i = 0; i < count; i++) \ + { \ + getsets[i].name = closures[i].name; \ + getsets[i].doc = closures[i].doc; \ + getsets[i].closure = &closures[i]; \ + getsets[i].get = SPy_Get; \ + getsets[i].set = SPy_Set; \ + } \ + PyType##Type.tp_base = &ScreenObjectType; \ + PyType##Type.tp_getset = getsets; \ + PyType##Type.tp_methods = methods; \ + PyType##Type.tp_compare = compare_##type; \ + PyType##Type.tp_repr = repr_##type; \ + PyType_Ready(&PyType##Type); \ + Py_INCREF(&PyType##Type); \ + PyModule_AddObject(module, #Type, (PyObject *)&PyType##Type); \ + return 1; \ +} + +#define DEFINE_TYPE(str, Type) \ +typedef struct \ +{ \ + ScreenObject __parent; \ + str *_obj; \ +} Py##Type; \ +\ +static PyTypeObject PyType##Type = \ +{ \ + PyObject_HEAD_INIT(NULL) \ + .ob_size = 0, \ + .tp_name = "screen." #Type, \ + .tp_basicsize = sizeof(Py##Type), \ + .tp_flags = Py_TPFLAGS_DEFAULT, \ + .tp_doc = #Type " object", \ + .tp_methods = NULL, \ + .tp_getset = NULL, \ +}; \ +\ +static PyObject * \ +PyObject_From##Type(str *_obj) \ +{ \ + Py##Type *obj = PyType##Type.tp_alloc(&PyType##Type, 0); \ + obj->_obj = _obj; \ + return (PyObject *)obj; \ +} + +static PyObject * +PyString_FromStringSafe(const char *str) +{ + if (str) + return PyString_FromString(str); + RETURN_NONE; +} + +/** Generic Object {{{ */ +typedef struct +{ + PyObject_HEAD + char *name; +} ScreenObject; + +static PyObject * +object_hook(PyObject *self, PyObject *args, PyObject *kw) +{ + char *object; + object = *(char **)((char *)self + sizeof(ScreenObject)); /* ugliness */ + return register_event_hook(self, args, kw, object); +} + +static PyMethodDef omethods[] = { + {"hook", (PyCFunction)object_hook, METH_VARARGS | METH_KEYWORDS, "Hook to an event on the object."}, + {NULL} +}; + +static PyTypeObject ScreenObjectType = +{ + PyObject_HEAD_INIT(NULL) + .ob_size = 0, + .tp_name = "screen.Object", + .tp_basicsize = sizeof(ScreenObject), + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .tp_doc = "Generic object", + .tp_methods = omethods, + .tp_getset = NULL, +}; + +static int +register_object(PyObject *module) +{ + PyType_Ready(&ScreenObjectType); + Py_INCREF(&ScreenObjectType); + PyModule_AddObject(module, "Generic Object", (PyObject *)&ScreenObjectType); +} + +/** }}} */ + +/** Window {{{ */ +DEFINE_TYPE(struct win, Window) + +static int +window_set_title(struct win *win, PyObject *value) +{ + char *string = PyString_AsString(value); + if (!string || !*string) + return -1; + ChangeAKA(win, string, strlen(string)); + return 0; +} + +#define SPY_CLOSURE(name, doc, type, member, func, setter) \ + {name, doc, type, offsetof(PyWindow, _obj), offsetof(struct win, member), func, setter} +static SPyClosure wclosures[] = +{ + SPY_CLOSURE("title", "Window title", T_STRING, w_title, NULL, window_set_title), + SPY_CLOSURE("number", "Window number", T_INT, w_number, NULL, NULL), + SPY_CLOSURE("dir", "Window directory", T_STRING, w_dir, NULL, NULL), + SPY_CLOSURE("tty", "TTY belonging to the window", T_STRING_INPLACE, w_tty, NULL, NULL), + SPY_CLOSURE("group", "The group the window belongs to", T_OBJECT_EX, w_group, PyObject_FromWindow, NULL), + SPY_CLOSURE("pid", "Window pid", T_INT, w_pid, NULL, NULL), + {NULL} +}; + +static PyObject * +window_select(PyObject *self) +{ + PyWindow *win = self; + struct win *w = win->_obj; + SwitchWindow(w->w_number); + RETURN_NONE; +} + +static PyMethodDef wmethods[] = { + {"select", (PyCFunction)window_select, METH_NOARGS, "Select the window."}, + {NULL}, +}; + +static int +compare_window(PyWindow *one, PyWindow *two) +{ + struct win *wone = one->_obj; + struct win *wtwo = two->_obj; + + return wtwo->w_number - wone->w_number; +} + +static PyObject * +repr_window(PyObject *obj) +{ + PyWindow *w = obj; + struct win *win = w->_obj; + return PyString_FromFormat("window (title: %s, number: %d)", win->w_title, win->w_number); +} + +REGISTER_TYPE(window, Window, wclosures, wmethods) +#undef SPY_CLOSURE +/** }}} */ + +/** Display {{{ */ +DEFINE_TYPE(struct display, Display) + +#define SPY_CLOSURE(name, doc, type, member, func) \ + {name, doc, type, offsetof(PyDisplay, _obj), offsetof(struct display, member), func} +static SPyClosure dclosures[] = +{ + SPY_CLOSURE("tty", "Display TTY", T_STRING_INPLACE, d_usertty, NULL), + SPY_CLOSURE("term", "Display Term", T_STRING_INPLACE, d_termname, NULL), + SPY_CLOSURE("fore", "Foreground window of the display", T_OBJECT_EX, d_fore, PyObject_FromWindow), + SPY_CLOSURE("width", "Display width", T_INT, d_width, NULL), + SPY_CLOSURE("height", "Display height", T_INT, d_height, NULL), + {NULL} +}; + +static PyMethodDef dmethods[] = +{ + {NULL} +}; + +static int +compare_display(PyDisplay *one, PyDisplay *two) +{ + struct display *done = one->_obj; + struct display *dtwo = two->_obj; + struct display *iter; + + if (done->d_userpid == dtwo->d_userpid) + return 0; + + for (iter = displays; iter; iter = iter->d_next) + if (iter == done) + return -1; + else if (iter == dtwo) + return 1; + + return 0; +} + +static PyObject * +repr_display(PyObject *obj) +{ + PyDisplay *d = obj; + struct display *disp = d->_obj; + return PyString_FromFormat("display (term: %s, tty: %s)", disp->d_termname, disp->d_usertty); +} + +REGISTER_TYPE(display, Display, dclosures, dmethods) +#undef SPY_CLOSURE +/** }}} */ + +/** Callback {{{ */ +DEFINE_TYPE(SPyCallback, Callback) +static SPyClosure cclosures[] = {{NULL}}; + +static void +FreeCallback(SPyCallback *scallback) +{ + Py_XDECREF(scallback->callback); + Free(scallback); +} + +static PyObject * +callback_unhook(PyObject *obj) +{ + PyCallback *cb = obj; + SPyCallback *scallback = cb->_obj; + if (!scallback) + return NULL; + unregister_listener(scallback->listener); + FreeCallback(scallback); + cb->_obj = NULL; + RETURN_NONE; +} + +static PyMethodDef cmethods[] = { + {"unhook", (PyCFunction)callback_unhook, METH_NOARGS, "Unhook this event callback."}, + {NULL} +}; +REGISTER_TYPE(callback, Callback, cclosures, cmethods) +/** }}} */ + +static PyObject * +SPy_Get(PyObject *obj, void *closure) +{ + SPyClosure *sc = closure; + char **first = (char *)obj + sc->offset1; + char **second = (char *)*first + sc->offset2; + PyObject *(*cb)(void *) = sc->conv; + void *data = *second; + + if (!cb) + { + switch (sc->type) + { + case T_STRING: + cb = PyString_FromStringSafe; + data = *second; + break; + case T_STRING_INPLACE: + cb = PyString_FromStringSafe; + data = second; + break; + case T_INT: + cb = PyInt_FromLong; + data = *second; + break; + } + } + return cb(data); +} + +static int +SPy_Set(PyObject *obj, PyObject *value, void *closure) +{ + SPyClosure *sc = closure; + char **first = (char *)obj + sc->offset1; + if (value == NULL) + { + PyErr_SetString(PyExc_TypeError, "Cannot remove an attribute value."); + return -1; + } + if (sc->setter == NULL) + { + char str[256]; + snprintf(str, sizeof(str) - 1, "Cannot change '%s'.", sc->name); + PyErr_SetString(PyExc_TypeError, str); + return -1; + } + + return sc->setter(*(void **)first, value); +} + +static int +PyDispatch(void *handler, const char *params, va_list va) +{ + PyCallback *callback = handler; + PyObject *args, *ret; + int count, retval; + const char *p; + SPyCallback *scallback = callback->_obj; + + for (count = 0, p = params; *p; p++, count++) + ; + if (count > 0) + args = PyTuple_New(count); + else + args = NULL; + + for (count = 0, p = params; *p; p++, count++) + { + PyObject *item = NULL; + switch (*p) + { + case 's': + item = PyString_FromStringSafe(va_arg(va, char *)); + break; + case 'S': + { + char **ls = va_arg(va, char **), **iter; + int c = 0; + for (iter = ls; iter && *iter; iter++, c++) + ; + if (c == 0) + break; + item = PyTuple_New(c); + for (c = 0, iter = ls; iter && *iter; iter++, c++) + PyTuple_SetItem(item, c, PyString_FromStringSafe(*iter)); + } + break; + case 'i': + item = PyInt_FromLong(va_arg(va, int)); + break; + case 'd': + item = PyObject_FromDisplay(va_arg(va, struct display *)); + break; + } + + if (!item) + { + item = Py_None; + Py_INCREF(Py_None); + } + PyTuple_SetItem(args, count, item); + } + + ret = PyObject_CallObject(scallback->callback, args); + Py_DECREF(args); + if (!ret) + return 0; + + retval = (int)PyInt_AsLong(ret); + Py_DECREF(ret); + return retval; +} + +static PyObject * +register_event_hook(PyObject *self, PyObject *args, PyObject *kw, void *object) +{ + static char *kwlist[] = {"event", "callback", NULL}; + + PyObject *callback; + char *name; + + struct script_event *sev; + struct listener *l; + SPyCallback *scallback; + + if (!PyArg_ParseTupleAndKeywords(args, kw, "sO:screen.hook", kwlist, &name, &callback)) + return NULL; /* Return Py_None instead? */ + + if (!PyCallable_Check(callback)) + { + PyErr_SetString(PyExc_TypeError, "The event-callback functions must be callable."); + LMsg(0, "The event-callback functions must be callable."); + return NULL; + } + + sev = object_get_event(object, name); + if (!sev) + { + LMsg(0, "No event named '%s'", name); + return NULL; + } + + l = malloc(sizeof(struct listener)); + + scallback = malloc(sizeof(SPyCallback)); + scallback->callback = callback; + scallback->listener = l; + Py_INCREF(scallback->callback); + + l->handler = PyObject_FromCallback(scallback); + l->priv = 0; + l->dispatcher = PyDispatch; + if (register_listener(sev, l)) + { + Py_DECREF((PyObject *)l->handler); + FreeCallback(scallback); + Free(l); + + LMsg(0, "Hook could not be registered."); + + RETURN_NONE; + } + + Py_INCREF((PyObject *)l->handler); + return l->handler; + + RETURN_NONE; +} + +/** Screen {{{ */ +static PyObject * +screen_display(PyObject *self) +{ + if (!display) + { + RETURN_NONE; + } + return PyObject_FromDisplay(display); +} + +static PyObject * +screen_displays(PyObject *self) +{ + struct display *d = displays; + int count = 0; + for (; d; d = d->d_next) + ++count; + PyObject *tuple = PyTuple_New(count); + + for (d = displays, count = 0; d; d = d->d_next, ++count) + PyTuple_SetItem(tuple, count, PyObject_FromDisplay(d)); + + return tuple; +} + +static PyObject * +screen_windows(PyObject *self) +{ + struct win *w = windows; + int count = 0; + for (; w; w = w->w_next) + ++count; + PyObject *tuple = PyTuple_New(count); + + for (w = windows, count = 0; w; w = w->w_next, ++count) + PyTuple_SetItem(tuple, count, PyObject_FromWindow(w)); + + return tuple; +} + +static PyObject * +hook_event(PyObject *self, PyObject *args, PyObject *kw) +{ + return register_event_hook(self, args, kw, NULL); +} + +static void +screen_input_cb(char *buf, int len, char *p) +{ + PyObject *callback = p; + PyObject *str = PyTuple_New(1); + PyTuple_SetItem(str, 0, PyString_FromStringSafe(buf)); + PyObject_CallObject(callback, str); + Py_DECREF(str); + Py_DECREF(callback); +} + +static PyObject * +screen_input(PyObject *self, PyObject *args, PyObject *kw) +{ + static char *kwlist[] = {"prompt", "callback", "value (optional)", NULL}; + char *prompt, *pre = NULL; + PyObject *callback; + + if (!PyArg_ParseTupleAndKeywords(args, kw, "sO|s:screen.input", kwlist, &prompt, &callback, &pre)) + { + LMsg(0, "Could not parse all the parameters to screen.input call."); + return NULL; + } + + if (!PyCallable_Check(callback)) + { + LMsg(0, "Input callback must be a callable object."); + return NULL; + } + + Py_INCREF(callback); + Input(prompt, 100 /* huh? */, + INP_COOKED, screen_input_cb, callback, 0); + + if (pre && *pre) + { + int len = strlen(pre); + LayProcess(&pre, &len); + } + + RETURN_NONE; +} + +const PyMethodDef py_methods[] = { + {"display", (PyCFunction)screen_display, METH_NOARGS, "Get the current display."}, + {"displays", (PyCFunction)screen_displays, METH_NOARGS, "Get the list of displays."}, + {"hook", (PyCFunction)hook_event, METH_VARARGS|METH_KEYWORDS, "Hook a callback to an event."}, + {"input", (PyCFunction)screen_input, METH_VARARGS|METH_KEYWORDS, "Read user input interactively."}, + {"windows", (PyCFunction)screen_windows, METH_NOARGS, "Get the list of windows."}, + {NULL, NULL, 0, NULL} +}; +/** }}} */ + +static PyObject *module; +static int +SPyInit(void) +{ + PyObject *m; + + Py_Initialize(); + + m = Py_InitModule3 ("screen", py_methods, NULL); + register_object(m); + register_window(m); + register_display(m); + register_callback(m); + module = m; + + return 0; +} + +static int +SPyFinit(void) +{ + Py_Finalize(); + return 0; +} + +static int +SPySource(const char *file, int async) +{ + FILE *f = fopen(file, "rb"); + int ret = PyRun_SimpleFile(f, file); + fclose(f); + + if (ret == 0) + return 1; /* Success */ + + if (PyErr_Occurred()) + { + PyErr_Print(); + return 0; + } + + return 1; +} + +static int +SPyCall(char *func, char **argv) +{ + PyObject *dict = PyModule_GetDict(module); + PyObject *f = PyDict_GetItemString(dict, func); + PyObject *ret; + if (!f) + { + return 0; + } + ret = PyEval_CallFunction(f, "s", argv[0]); + Py_XDECREF(ret); + return 1; +} + +struct binding py_binding = +{ + "python", + 0, + 0, + SPyInit, + SPyFinit, + SPyCall, + SPySource, + 0, + 0 +}; + diff --git a/src/script.c b/src/script.c index 2b09a9c..6f20bc4 100644 --- a/src/script.c +++ b/src/script.c @@ -21,6 +21,7 @@ #include "config.h" #include "screen.h" +#include "extern.h" #include <stddef.h> /*Binding structure & functions*/ @@ -42,11 +43,19 @@ register_binding (struct binding *new_binding) extern struct binding lua_binding; #endif +#ifdef PY_BINDING +extern struct binding py_binding; +#endif + void LoadBindings(void) { #ifdef LUA_BINDING register_binding(&lua_binding); #endif + +#ifdef PY_BINDING + register_binding(&py_binding); +#endif } void @@ -70,7 +79,7 @@ ScriptSource(int argc, const char **argv) struct binding *binding = bindings; /* Parse the commandline options - * sourcescript [-async|-a] [-binding|-b <binding>] script + * script source [-async|-a] [-binding|-b <binding>] script */ while (*argv && **argv == '-') { @@ -81,38 +90,75 @@ ScriptSource(int argc, const char **argv) async = 1; /* check for (-b | -binding) */ else if ((arg[1] == 'b' && !arg[2]) - || strcmp(arg, "-binding") == 0) { + || strcmp(arg, "-binding") == 0) + { argv++; bd_select = *argv; - } + } argv++; - } + } script = *argv; - while (binding) { - if (!bd_select || strcmp(bd_select, binding->name) == 0) { + while (binding) + { + if (!bd_select || strcmp(bd_select, binding->name) == 0) + { /*dynamically initialize the binding*/ if (!binding->inited) - { - binding->bd_Init(); - binding->inited = 1; - } + { + binding->bd_Init(); + binding->inited = 1; + } /*and source the script*/ - if (ret = binding->bd_Source(script, async)) + ret = binding->bd_Source(script, async); + if (ret) break; - } + } binding = binding->b_next; - } + } if (!ret) - LMsg(1, "Could not source specified script %s", script); + LMsg(0, "Could not source specified script %s", script); } int -ScriptCall(const char *func, const char **argv) +ScriptCall(int argc, const char **argv) { - /*TODO*/ - return LuaCall(func, argv); + int ret = 0; + struct binding *binding = bindings; + const char *bd_select = 0, *func; + /* Parse the commandline options + * script call [-binding|-b <binding>] function + */ + while (*argv && **argv == '-') + { + const char *arg = *argv; + /* check for (-b | -binding) */ + if ((arg[1] == 'b' && !arg[2]) + || strcmp(arg, "-binding") == 0) + { + argv++; + bd_select = *argv; + } + argv++; + } + func = *argv; + argv++; + + while (binding) + { + if (!bd_select || strcmp(bd_select, binding->name) == 0) + { + ret = binding->bd_call(func, argv); + if (ret) + break; + } + binding = binding->b_next; + } + + if (!ret) + LMsg(0, "Failed to run specified script coummand '%s'", func); + return ret; } void @@ -121,7 +167,7 @@ ScriptCmd(int argc, const char **argv) const char * sub = *argv; argv++;argc--; if (!strcmp(sub, "call")) - ScriptCall(*argv, argv+1); + ScriptCall(argc, argv); else if (!strcmp(sub, "source")) ScriptSource(argc, argv); } diff --git a/src/script.h b/src/script.h index c268eb5..dd37899 100644 --- a/src/script.h +++ b/src/script.h @@ -37,7 +37,7 @@ struct binding int registered; int (*bd_Init) __P((void)); int (*bd_Finit) __P((void)); - int (*bd_call) __P((char *func, char **argv)); + int (*bd_call) __P((const char *func, const char **argv)); /*Returns zero on failure, non zero on success*/ int (*bd_Source) __P((const char *, int)); struct binding *b_next; diff --git a/src/scripts/callback.py b/src/scripts/callback.py new file mode 100644 index 0000000..1c517f5 --- /dev/null +++ b/src/scripts/callback.py @@ -0,0 +1,18 @@ +import screen + +ticket = None + +def cmd_cb(event, params): + f = open("/tmp/debug/py", "ab") + f.write("Event triggered: %s (%s)\n" % (event, params)) + f.close() + return 0 + +ticket = screen.hook("global_cmdexecuted", cmd_cb) + +def detached_cb(display, flags): + ticket.unhook() + return 0 + +screen.hook("global_detached", detached_cb) + diff --git a/src/scripts/input.py b/src/scripts/input.py new file mode 100644 index 0000000..7bac375 --- /dev/null +++ b/src/scripts/input.py @@ -0,0 +1,9 @@ +import screen + +def input_cb(str): + f = open("/tmp/debug/py", "ab") + f.write("%s\n" % str) + f.close() + +screen.input("Test:", input_cb, "123") + diff --git a/src/scripts/py.py b/src/scripts/py.py new file mode 100644 index 0000000..106c465 --- /dev/null +++ b/src/scripts/py.py @@ -0,0 +1,13 @@ +import time +import screen + +f = open("/tmp/debug/py", "ab") +f.write("Called at %s \n" % (time.asctime(time.localtime()))) +f.close() + + +f = open("/tmp/debug/py", "ab") +windows = screen.windows() +for win in windows: + f.write("Window: %s (%d) %s %s %d\n" % (win.title, win.number, win.dir, win.tty, win.pid)) +f.close() |