summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSadrul Habib Chowdhury <sadrul@users.sourceforge.net>2010-04-03 04:05:40 -0400
committerSadrul Habib Chowdhury <sadrul@users.sourceforge.net>2010-04-03 04:05:40 -0400
commitdf282d7e09534e2be982d134447b29b023fc0d1c (patch)
tree2c2db2074eaadec1986f72c978b52efc230c13d5
parent3d5dc020e65450446f247ca7b4ea1c7fc5649aa7 (diff)
parentc250a4aa1caf7bc955407e2846149632a84a1f46 (diff)
downloadscreen-df282d7e09534e2be982d134447b29b023fc0d1c.tar.gz
Merge branch 'scripting-python' into scripting
Conflicts: src/Makefile.in
-rw-r--r--src/Makefile.in8
-rw-r--r--src/acconfig.h1
-rw-r--r--src/acinclude.m4210
-rw-r--r--src/configure.in3
-rw-r--r--src/python.c659
-rw-r--r--src/script.c82
-rw-r--r--src/script.h2
-rw-r--r--src/scripts/callback.py18
-rw-r--r--src/scripts/input.py9
-rw-r--r--src/scripts/py.py13
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()