summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Makefile.in10
-rw-r--r--src/acconfig.h7
-rw-r--r--src/comm.c4
-rw-r--r--src/configure.in12
-rw-r--r--src/drafts/screen_internal95
-rw-r--r--src/drafts/scripting276
-rw-r--r--src/help.c2
-rw-r--r--src/lua.c1034
-rw-r--r--src/process.c282
-rw-r--r--src/screen.c72
-rw-r--r--src/screen.h1
-rw-r--r--src/script.c290
-rw-r--r--src/script.h89
-rw-r--r--src/scripts/cmdcallback.lua44
-rw-r--r--src/scripts/findwindow.lua19
-rw-r--r--src/window.h7
16 files changed, 2222 insertions, 22 deletions
diff --git a/src/Makefile.in b/src/Makefile.in
index 2ae57d0..58b961a 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@
+CFLAGS = @CFLAGS@ @LUA_CFLAGS@
CPPFLAGS = @CPPFLAGS@ -DETCSCREENRC='"$(ETCSCREENRC)"' \
-DSCREENENCODINGS='"$(SCREENENCODINGS)"'
LDFLAGS = @LDFLAGS@
-LIBS = @LIBS@
+LIBS = @LIBS@ @LUA_LIBS@
CPP=@CPP@
CPP_DEPEND=$(CC) -MM
@@ -62,13 +62,15 @@ CFILES= screen.c ansi.c fileio.c mark.c misc.c resize.c socket.c \
termcap.c input.c attacher.c pty.c process.c display.c comm.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
+ list_display.c list_generic.c list_window.c \
+ lua.c script.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
+ sched.o teln.o nethack.o encoding.o canvas.o layout.o viewport.o \
+ lua.o script.o
all: screen
diff --git a/src/acconfig.h b/src/acconfig.h
index 2e46985..585de3d 100644
--- a/src/acconfig.h
+++ b/src/acconfig.h
@@ -140,6 +140,7 @@
* Syntax: screen //telnet host [port]
* define RXVT_OSC if you want support for rxvts special
* change fgcolor/bgcolor/bgpicture sequences
+ * define SCRIPT to add scripting support to screen.
*/
#undef SIMPLESCREEN
#ifndef SIMPLESCREEN
@@ -161,8 +162,14 @@
# define COLORS16
# define ZMODEM
# define BLANKER_PRG
+# define SCRIPT
#endif /* SIMPLESCREEN */
+/*Include the binding you would like to use.*/
+#ifdef SCRIPT
+#define LUA_BINDING
+#endif
+
#undef BUILTIN_TELNET
#undef RXVT_OSC
#undef COLORS256
diff --git a/src/comm.c b/src/comm.c
index 5f4af8a..5ec1530 100644
--- a/src/comm.c
+++ b/src/comm.c
@@ -56,6 +56,7 @@ struct comm comms[RC_LAST + 1] =
#ifdef MULTIUSER
{ "addacl", ARGS_1234 },
#endif
+ { "alias", ARGS_12|ARGS_ORMORE },
{ "allpartial", NEED_DISPLAY|ARGS_1 },
{ "altscreen", ARGS_01 },
{ "at", ARGS_2|ARGS_ORMORE },
@@ -283,6 +284,9 @@ struct comm comms[RC_LAST + 1] =
{ "reset", NEED_FORE|ARGS_0 },
{ "resize", NEED_DISPLAY|ARGS_0|ARGS_ORMORE },
{ "screen", ARGS_0|ARGS_ORMORE },
+#ifdef SCRIPT
+ { "script", ARGS_2|ARGS_ORMORE },
+#endif
#ifdef COPY_PASTE
{ "scrollback", NEED_FORE|ARGS_1 },
#endif
diff --git a/src/configure.in b/src/configure.in
index bd29de2..4cf9376 100644
--- a/src/configure.in
+++ b/src/configure.in
@@ -1292,6 +1292,18 @@ fi
dnl Ptx bug workaround -- insert -lc after -ltermcap
test -n "$seqptx" && LIBS="-ltermcap -lc -lsocket -linet -lnsl -lsec -lseq"
+PKG_CHECK_MODULES(LUA, [lua5.1], , [
+ AC_MSG_RESULT(no)
+ AC_MSG_ERROR([
+
+*** You need to have lua5.1 (http://www.lua.org/) installed.
+*** If you don't want support for lua scripting, please use the official repository
+*** (http://git.savannah.gnu.org/gitweb/?p=screen.git;a=summary).
+])])
+
+AC_SUBST(LUA_CFLAGS)
+AC_SUBST(LUA_LIBS)
+
AC_TRY_RUN(main(){exit(0);},,AC_MSG_ERROR(Can't run the compiler - internal error. Sorry.))
AC_OUTPUT(Makefile doc/Makefile, [[
diff --git a/src/drafts/screen_internal b/src/drafts/screen_internal
new file mode 100644
index 0000000..8299986
--- /dev/null
+++ b/src/drafts/screen_internal
@@ -0,0 +1,95 @@
+I. Disclaimer
+
+This document includes some of my personal understanding about the internal
+structure of GNU Screen. It's written to serve as a remainder to myself. I
+hope that it can help those not familiar with GNU Screen already. But be aware
+of any possible mistake in this document.
+
+
+II. Architecture
+
+GNU Screen works in a client-server way. The two participants are called
+front-end and back-end respectively, sharing the same code base. When an
+instance of Screen launches, the command line arguments determines the role
+and the corresponding action.
+
+Front-end send requests to back-end through a named-pipe or socket. And the
+back-end gives feed back by signal. The requests include attach, detach,
+resize, window create and command execution. The front-end does nothing more
+than send requests and act upon responses. When there are no such events, it
+simply sleeps to kill it's boring time. All other works are handled by the
+back-end.
+
+
+III. Important objects
+
+There are several kinds of objects in Screen. Here are some of them.
+
+A. Display
+
+A Display stands for an attached display area. It corresponds to the real user
+tty that the attaching front-end runs in. Since there can be multiple users
+(or multiple attach from the same user), the possibly multiple displays are
+chained together in a single linked-list 'displays'. An Display object
+contains many global statuses and flags (terminal modes & flags etc.), also
+includes some events such as tty read & write.
+
+B. Canvas, Viewport & Layout
+
+Canvas is a place to draw contents on. They logically belong to a specific
+display. However, a display can be further divided to several sub-regions,
+using the split command. As a result, the regions need corresponding canvases.
+Moreover, the way that a display is organised can be saved by Layout object.
+All Layout objects are linked together as a list. The active layout used by
+Display is stored with it. The canvas in a display that has input focus is
+called forecv.
+
+To track the Canvases in a Display, Screen uses a two-dimensional link list.
+One list link (slnext & slprev) together all adjacent Canvases that are
+spitted in the same direction. The other is the stacking direction, or the
+Z-axis in 3D graphics. All such adjacent Canvases shares one container Canvas,
+which is inserted when a split with different direction is about to happen.
+Each Canvas points to it's container using slback and a container points to
+one of its random child using slprep. This structure actually link the
+Canvases together as a tree, which is very helpful when doing free and resize.
+
+Finally, to ease the clipping when drawing in the canvas, there is also a
+viewport object to track the boundary of each canvas. (But why not the canvas
+itself?)
+
+C. Window & Layer
+
+Each Window in screen stands for a pseudo-terminal (or telnet connection,
+depending on the type of window) that runs inside screen. All relevant
+statuses and events are stored in this object. Many Windows can be shown in
+Display at once, the one that has input focus is called the fore Window.
+
+To be shown in Canvases, a Window needs a object called Layer. A Layer can be
+drawn on multiple Canvases, either in one Display or in different Displays. So
+it also make sense to call it 'view'. However, the name Layer tells us that
+it's stackable. One can add an overlay to the existing one, such as the help
+page. Each Layer contains an operation function structure, providing important
+operation to process the input, draw line, clear, redraw, resize, abort and
+restore etc.
+
+E users & ACL
+
+TODO
+
+IV. Event dispatching.
+
+Screen uses asynchronous IO to handle all inputs/outputs in all ttys. Such
+asynchronous events, IO related or not, are organized as events, registered to
+a central event list and got scheduled by a scheduler.
+
+The scheduler loops indefinitely. In each turn, it waits for some events to go
+ready using the Select() system call. There are three types of events,
+READ/WRITE, TIMEOUT and ALWAYS. The READ/WRITE events are mostly used to carry
+data between outer tty and ptys within screen. And the TIMEOUT events are
+mostly used to do periodically update. Different from other kinds of events,
+the TIMEOUT event is one-off, and should be re-scheduled if periodic
+activation is desired. Note that the TIMEOUT event has lower priority than
+READ/WRITE events, and the timeout specified is never adjusted to compensate
+the elapsed time. As a result, the period of activation should typically
+longer then specified.
+
diff --git a/src/drafts/scripting b/src/drafts/scripting
new file mode 100644
index 0000000..a126906
--- /dev/null
+++ b/src/drafts/scripting
@@ -0,0 +1,276 @@
+===================
+The design
+===================
+
+I. Key Factors
+There are several aspects to deal with when adding scripting support to
+Screen:
+1. The set of information to expose.
+2. The command interface to provide.
+3. The hook on interesting events in Screen.
+4. The ability to do run asynchronously to Screen itself.
+
+A. Query & control interface
+
+Currently, screen provides a way to control a running session through the -X
+command switch, which sends the specified command through the sever socket.
+It's quite useful and can already do automation to some degree, (e.g. [1]).
+However, the problem is that, there is no way to get enough feedback to make
+decision in your script in such a scheme. The first job is to provide an
+interface for scripts to query internal status of Screen. The second job is to
+provide an interface to control Screen, with reasonable feedback.
+
+B. Event hooking
+
+When the internal status of Screen changed, we say that an event happened.
+Events are also interesting to scripts, but are not covered by the query
+interface. A hook / callback system is needed to notify scripts about
+interested events. With the callback that triggers before an event, the script
+should be able to stop the event from happening.
+
+C. Asynchronous scripting
+
+So far, with the described scripting support, we can write small functions
+that provide enhanced functionality to a screen command, or write event
+handler that handles interested events. But we are restricted to finish our
+work quickly. The problem is that, Screen itself is a single threaded
+application, utilizing asynchronous I/O to provide services to all windows
+running inside. So you simply can not do time-consuming work in any of the
+processing path, including the scripts.
+
+Sometimes we may need to wait for something in a script, such as emulating
+Expect in a Screen script [2]. The core of this use case is to wait for an
+output pattern and react upon it. One possible way is to provide an event
+callback for it. But it's not a pleasant way to do a series of interaction.
+A natural solution is to make the script able to run asynchronously to Screen
+itself.
+
+[1] http://github.com/rblackwe/yapc--na-2007--making-an-ajax-gui-for-gnu-screen
+[2] http://lists.gnu.org/archive/html/screen-users/2009-05/msg00006.html
+
+II. The Screen interface
+
+Screen needs to provide a user interface to source and run scripts.
+
+script source [-async|-a] [-binding|-b <binding>] script.
+
+ This command sources the specified script. This command can be used several
+ times to source multiple scripts. Use the -async switch if the
+ script is supposed to run asynchronously , e.g. it needs to wait for
+ external events. Asynchronous scripts running mode needs to acquire a
+ lock before playing on Screen itself. As a result, it pays more overhead
+ than synch ones. Moreover, asynchronous scripts may be isolated from other
+ scripts and may not share any information with them. In other words, normal
+ scripts may share the same context. (Note: the isolation between scripts may
+ be implementation dependent. Which is more desirable?)
+
+script call func arg1 arg2 ...
+
+ Call functions defined by scripts. If the same function are defined in
+ multiple scripting context, the last one applies. Call to normal script
+ returns synchronously. And call to asynchronous one will return immediately.
+
+Special invoke interface, such as those embedded in caption/hstatus format
+string.
+
+III. The scripting interface
+
+There are several kinds of objects that may be interested to scripts.
+
+A. Display
+
+Stands for a user interface.
+
+----------
+Properties:
+----------
+
+tty:
+ mode: read only.
+ type: String.
+ description: The tty that this attach runs on.
+
+term:
+ mode: read only.
+ type: String.
+ description: The TERM env of that tty.
+
+fore:
+ mode: read only.
+ type: Window.
+ description: The fore window.
+
+other:
+ mode: read only.
+ type: Window.
+ description: List of windows other than the fore.
+
+width:
+ mode: read only.
+ type: Int.
+ description: As the name suggests
+
+height:
+ mode: read only.
+ type: Int.
+ description: As the name suggests
+
+user:
+ mode: read only.
+ type: User.
+ description: The owner of this display.
+
+layout:
+ mode: read only.
+ type: Layout.
+ description: Active layout on this display.
+
+
+----------
+Methods:
+----------
+
+get_canvases: Get a list of canvases on this display.
+
+----------
+Events:
+----------
+
+on_resize: Triggered after the geometry of the display is changed.
+
+B. Window
+
+Stands for the sub-terminal(PTY, telnet etc) that processes runs on.
+
+----------
+Properties:
+----------
+
+title:
+ mode: read write
+ type: String.
+ description: The title of the window.
+
+number:
+ mode: read only
+ type: int.
+ description: The index in the window slots.
+
+dir:
+ mode: read only
+ type: String.
+ description: The initial directory of the first
+ application (typically the shell) in that window.
+
+tty:
+ mode: read only
+ type: String
+ description: the associated terminal device for that window.
+
+pid:
+ mode: read only
+ type: int
+ description: the pid of of the slave end of the pty.
+
+group:
+ mode: read only
+ type: Window
+ description: The window group we belongs to. (*This seems in-active?*)
+
+bell:
+ mode: read only
+ type: int
+ description: The bell status of the window.
+
+----------
+Methods:
+----------
+
+int get_monitor_status(bool activity);
+ Returns the status of the specified monitor type. Either activity or silence.
+ When the silence monitor is active, the threshold is returned.
+
+void set_monitor_status(bool activity, int status);
+ Set the status of activity/silence monitor. The number of status for the
+ silence monitor is the seconds of threshold.
+
+void stuff(string buf);
+ put the string into the input buffer.
+
+int waitfor(string pattern);
+ Waiting for a specified output pattern. The pattern is limited to plain text.
+ NOTICE: This is an asynchronous method call and can only be called in
+ asynchronous mode.
+
+----------
+Events
+----------
+
+on_activity
+on_silence
+before_winchange
+on_winchange
+before_resize
+on_resize
+
+C. User
+
+--------
+Property:
+--------
+
+name:
+ mode: read only
+ type: String.
+ description: The login name.
+
+uid:
+ mode: read only
+ type: int
+ description: The index in the ACL bit fields.
+
+esc:
+metaesc:
+ mode: read only
+ type: int
+ description: The escape character
+
+umask:
+ mode: read only
+ type: String.
+ description: The access for the window created by this user to other users.
+ The result will be in a form of 'rwx'.
+
+D. Screen
+
+ This is a pseudo object standing for the whole screen object. All other
+ objects can be obtained starting from this one.
+
+--------
+Methods:
+--------
+windows
+displays
+command
+windowbyname
+
+input
+get input from user.
+
+
+===================
+The Implementation
+===================
+
+Bindings are in fact script interpretors. We can have several different
+language bindings at the same time, with each registered at the compiling time
+and loaded (initialized) dynamically at runtime. It's an bridge between
+scripts and screen itself.
+
+---------------
+Binding related.
+---------------
+
+---------------
+Binding independent
+---------------
diff --git a/src/help.c b/src/help.c
index 8726b1b..d766fee 100644
--- a/src/help.c
+++ b/src/help.c
@@ -182,7 +182,7 @@ struct action *ktabp;
for (key = 0; key < 256 + KMAP_KEYS; key++)
{
n = ktabp[key].nr;
- if (n == RC_ILLEGAL)
+ if (n == RC_ILLEGAL || n > RC_LAST)
continue;
if (ktabp[key].args == noargs)
{
diff --git a/src/lua.c b/src/lua.c
new file mode 100644
index 0000000..7b3ec71
--- /dev/null
+++ b/src/lua.c
@@ -0,0 +1,1034 @@
+/* Lua scripting support
+ *
+ * Copyright (c) 2008 Sadrul Habib Chowdhury (sadrul@users.sf.net)
+ * Copyright (c) 2009
+ * Sadrul Habib Chowdhury (sadrul@users.sf.net)
+ * Rui Guo (firemeteor.guo@gmail.com)
+ *
+ * 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 <sys/stat.h>
+#include <unistd.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "extern.h"
+#include "logfile.h"
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+
+static int StackDump(lua_State *L, const char *message)
+{
+ FILE *f = fopen("/tmp/debug/stack", "ab");
+ int i = lua_gettop(L);
+ if (message)
+ fprintf(f, "%s", message);
+ while (i)
+ {
+ int t = lua_type(L, i);
+ switch (t)
+ {
+ case LUA_TSTRING:
+ fprintf(f, "String: %s\n", lua_tostring(L, i));
+ break;
+
+ case LUA_TBOOLEAN:
+ fprintf(f, "Boolean: %s\n", lua_toboolean(L, i) ? "true" : "false");
+ break;
+
+ case LUA_TNUMBER:
+ fprintf(f, "Number: %g\n", lua_tonumber(L, i));
+ break;
+
+ default:
+ fprintf(f, "Type: %s\n", lua_typename(L, t));
+ }
+ i--;
+ }
+ if (message)
+ fprintf(f, "----\n");
+ fclose(f);
+ return 0;
+}
+
+extern struct win *windows, *fore;
+extern struct display *displays, *display;
+extern struct LayFuncs WinLf;
+extern struct layer *flayer;
+
+static int LuaDispatch(void *handler, const char *params, va_list va);
+static int LuaRegEvent(lua_State *L);
+static int LuaUnRegEvent(lua_State *L);
+
+enum
+{
+ LUA_HANDLER_TYPE_N = 1,
+ LUA_HANDLER_TYPE_F
+};
+
+struct lua_handler
+{
+ int type;
+ union
+ {
+ char *name;
+ int reference;
+ } u;
+};
+
+/** Template {{{ */
+
+#define CHECK_TYPE(name, type) \
+static type * \
+check_##name(lua_State *L, int index) \
+{ \
+ type **var; \
+ luaL_checktype(L, index, LUA_TUSERDATA); \
+ var = (type **) luaL_checkudata(L, index, #name); \
+ if (!var || !*var) \
+ luaL_typerror(L, index, #name); \
+ return *var; \
+}
+
+#define PUSH_TYPE(name, type) \
+static void \
+push_##name(lua_State *L, type **t) \
+{ \
+ if (!t || !*t) \
+ lua_pushnil(L); \
+ else \
+ { \
+ type **r; \
+ r = (type **)lua_newuserdata(L, sizeof(type *)); \
+ *r = *t; \
+ luaL_getmetatable(L, #name); \
+ lua_setmetatable(L,-2); \
+ } \
+}
+
+/* Much of the following template comes from:
+ * http://lua-users.org/wiki/BindingWithMembersAndMethods
+ */
+
+static int get_int (lua_State *L, void *v)
+{
+ lua_pushinteger (L, *(int*)v);
+ return 1;
+}
+
+static int set_int (lua_State *L, void *v)
+{
+ *(int*)v = luaL_checkint(L, 3);
+ return 0;
+}
+
+static int get_number (lua_State *L, void *v)
+{
+ lua_pushnumber(L, *(lua_Number*)v);
+ return 1;
+}
+
+static int set_number (lua_State *L, void *v)
+{
+ *(lua_Number*)v = luaL_checknumber(L, 3);
+ return 0;
+}
+
+static int get_string (lua_State *L, void *v)
+{
+ lua_pushstring(L, (char*)v );
+ return 1;
+}
+
+static int set_string (lua_State *L, void *v)
+{
+ *(const char**)v = luaL_checkstring(L, 3);
+ return 0;
+}
+
+typedef int (*Xet_func) (lua_State *L, void *v);
+
+/* member info for get and set handlers */
+struct Xet_reg
+{
+ const char *name; /* member name */
+ Xet_func func; /* get or set function for type of member */
+ size_t offset; /* offset of member within the struct */
+ int (*absolute)(lua_State *);
+};
+
+static void Xet_add (lua_State *L, const struct Xet_reg *l)
+{
+ if (!l)
+ return;
+ for (; l->name; l++)
+ {
+ lua_pushstring(L, l->name);
+ lua_pushlightuserdata(L, (void*)l);
+ lua_settable(L, -3);
+ }
+}
+
+static int Xet_call (lua_State *L)
+{
+ /* for get: stack has userdata, index, lightuserdata */
+ /* for set: stack has userdata, index, value, lightuserdata */
+ const struct Xet_reg *m = (const struct Xet_reg *)lua_touserdata(L, -1); /* member info */
+ lua_pop(L, 1); /* drop lightuserdata */
+ luaL_checktype(L, 1, LUA_TUSERDATA);
+ if (m->absolute)
+ return m->absolute(L);
+ return m->func(L, *(char**)lua_touserdata(L, 1) + m->offset);
+}
+
+static int index_handler (lua_State *L)
+{
+ /* stack has userdata, index */
+ lua_pushvalue(L, 2); /* dup index */
+ lua_rawget(L, lua_upvalueindex(1)); /* lookup member by name */
+ if (!lua_islightuserdata(L, -1))
+ {
+ lua_pop(L, 1); /* drop value */
+ lua_pushvalue(L, 2); /* dup index */
+ lua_gettable(L, lua_upvalueindex(2)); /* else try methods */
+ if (lua_isnil(L, -1)) /* invalid member */
+ luaL_error(L, "cannot get member '%s'", lua_tostring(L, 2));
+ return 1;
+ }
+ return Xet_call(L); /* call get function */
+}
+
+static int newindex_handler (lua_State *L)
+{
+ /* stack has userdata, index, value */
+ lua_pushvalue(L, 2); /* dup index */
+ lua_rawget(L, lua_upvalueindex(1)); /* lookup member by name */
+ if (!lua_islightuserdata(L, -1)) /* invalid member */
+ luaL_error(L, "cannot set member '%s'", lua_tostring(L, 2));
+ return Xet_call(L); /* call set function */
+}
+
+static int
+struct_register(lua_State *L, const char *name, const luaL_reg fn_methods[], const luaL_reg meta_methods[],
+ const struct Xet_reg setters[], const struct Xet_reg getters[])
+{
+ int metatable, methods;
+
+ /* create methods table & add it to the table of globals */
+ luaL_register(L, name, fn_methods);
+ methods = lua_gettop(L);
+
+ /* create metatable & add it to the registry */
+ luaL_newmetatable(L, name);
+ luaL_register(L, 0, meta_methods); /* fill metatable */
+
+ /* To identify the type of object */
+ lua_pushstring(L, "_objname");
+ lua_pushstring(L, name);
+ lua_settable(L, -3);
+
+ metatable = lua_gettop(L);
+
+ lua_pushliteral(L, "__metatable");
+ lua_pushvalue(L, methods); /* dup methods table*/
+ lua_rawset(L, metatable); /* hide metatable:
+ metatable.__metatable = methods */
+
+ lua_pushliteral(L, "__index");
+ lua_pushvalue(L, metatable); /* upvalue index 1 */
+ Xet_add(L, getters); /* fill metatable with getters */
+ lua_pushvalue(L, methods); /* upvalue index 2 */
+ lua_pushcclosure(L, index_handler, 2);
+ lua_rawset(L, metatable); /* metatable.__index = index_handler */
+
+ lua_pushliteral(L, "__newindex");
+ lua_newtable(L); /* table for members you can set */
+ Xet_add(L, setters); /* fill with setters */
+ lua_pushcclosure(L, newindex_handler, 1);
+ lua_rawset(L, metatable); /* metatable.__newindex = newindex_handler */
+
+ lua_pop(L, 1); /* drop metatable */
+ return 1; /* return methods on the stack */
+}
+
+/** }}} */
+
+/** Window {{{ */
+
+PUSH_TYPE(window, struct win)
+
+CHECK_TYPE(window, struct win)
+
+static int get_window(lua_State *L, void *v)
+{
+ push_window(L, (struct win **)v);
+ return 1;
+}
+
+static int
+window_change_title(lua_State *L)
+{
+ struct win *w = check_window(L, 1);
+ unsigned int len;
+ const char *title = luaL_checklstring(L, 2, &len);
+ ChangeAKA(w, (char *)title, len);
+ return 0;
+}
+
+static int
+window_get_monitor_status(lua_State *L)
+{
+ struct win *w = check_window(L, 1);
+ int activity = luaL_checkint(L, 2);
+ if (activity)
+ /*monitor*/
+ lua_pushinteger(L, w->w_monitor != MON_OFF);
+ else
+ /*silence*/
+ lua_pushinteger(L, w->w_silence == SILENCE_ON ? w->w_silencewait: 0);
+
+ return 1;
+}
+
+static const luaL_reg window_methods[] = {
+ {"get_monitor_status", window_get_monitor_status},
+ {"hook", LuaRegEvent},
+ {0, 0}
+};
+
+static int
+window_tostring(lua_State *L)
+{
+ char str[128];
+ struct win *w = check_window(L, 1);
+ snprintf(str, sizeof(str), "{window #%d: '%s'}", w->w_number, w->w_title);
+ lua_pushstring(L, str);
+ return 1;
+}
+
+static int
+window_equality(lua_State *L)
+{
+ struct win *w1 = check_window(L, 1), *w2 = check_window(L, 2);
+ lua_pushboolean(L, (w1 == w2 || (w1 && w2 && w1->w_number == w2->w_number)));
+ return 1;
+}
+
+static const luaL_reg window_metamethods[] = {
+ {"__tostring", window_tostring},
+ {"__eq", window_equality},
+ {0, 0}
+};
+
+static const struct Xet_reg window_setters[] = {
+ {"title", 0, 0, window_change_title/*absolute setter*/},
+ {0, 0}
+};
+
+static const struct Xet_reg window_getters[] = {
+ {"title", get_string, offsetof(struct win, w_title) + 8},
+ {"number", get_int, offsetof(struct win, w_number)},
+ {"dir", get_string, offsetof(struct win, w_dir)},
+ {"tty", get_string, offsetof(struct win, w_tty)},
+ {"pid", get_int, offsetof(struct win, w_pid)},
+ {"group", get_window, offsetof(struct win, w_group)},
+ {"bell", get_int, offsetof(struct win, w_bell)},
+ {0, 0}
+};
+
+
+/** }}} */
+
+/** AclUser {{{ */
+
+PUSH_TYPE(user, struct acluser)
+
+CHECK_TYPE(user, struct acluser)
+
+static int
+get_user(lua_State *L, void *v)
+{
+ push_user(L, (struct acluser **)v);
+ return 1;
+}
+
+static const luaL_reg user_methods[] = {
+ {0, 0}
+};
+
+static int
+user_tostring(lua_State *L)
+{
+ char str[128];
+ struct acluser *u = check_user(L, 1);
+ snprintf(str, sizeof(str), "{user '%s'}", u->u_name);
+ lua_pushstring(L, str);
+ return 1;
+}
+
+static const luaL_reg user_metamethods[] = {
+ {"__tostring", user_tostring},
+ {0, 0}
+};
+
+static const struct Xet_reg user_setters[] = {
+ {0, 0}
+};
+
+static const struct Xet_reg user_getters[] = {
+ {"name", get_string, offsetof(struct acluser, u_name)},
+ {"password", get_string, offsetof(struct acluser, u_password)},
+ {0, 0}
+};
+
+/** }}} */
+
+/** Canvas {{{ */
+
+PUSH_TYPE(canvas, struct canvas)
+
+CHECK_TYPE(canvas, struct canvas)
+
+static int
+get_canvas(lua_State *L, void *v)
+{
+ push_canvas(L, (struct canvas **)v);
+ return 1;
+}
+
+static int
+canvas_select(lua_State *L)
+{
+ struct canvas *c = check_canvas(L, 1);
+ if (!display || D_forecv == c)
+ return 0;
+ SetCanvasWindow(c, Layer2Window(c->c_layer));
+ D_forecv = c;
+
+ /* XXX: the following all is duplicated from process.c:DoAction.
+ * Should these be in some better place?
+ */
+ ResizeCanvas(&D_canvas);
+ RecreateCanvasChain();
+ RethinkDisplayViewports();
+ ResizeLayersToCanvases(); /* redisplays */
+ fore = D_fore = Layer2Window(D_forecv->c_layer);
+ flayer = D_forecv->c_layer;
+#ifdef RXVT_OSC
+ if (D_xtermosc[2] || D_xtermosc[3])
+ {
+ Activate(-1);
+ break;
+ }
+#endif
+ RefreshHStatus();
+#ifdef RXVT_OSC
+ RefreshXtermOSC();
+#endif
+ flayer = D_forecv->c_layer;
+ CV_CALL(D_forecv, LayRestore();LaySetCursor());
+ WindowChanged(0, 'F');
+ return 1;
+}
+
+static const luaL_reg canvas_methods[] = {
+ {"select", canvas_select},
+ {0, 0}
+};
+
+static const luaL_reg canvas_metamethods[] = {
+ {0, 0}
+};
+
+static const struct Xet_reg canvas_setters[] = {
+ {0, 0}
+};
+
+static int
+canvas_get_window(lua_State *L)
+{
+ struct canvas *c = check_canvas(L, 1);
+ struct win *win = Layer2Window(c->c_layer);
+ if (win)
+ push_window(L, &win);
+ else
+ lua_pushnil(L);
+ return 1;
+}
+
+static const struct Xet_reg canvas_getters[] = {
+ {"next", get_canvas, offsetof(struct canvas, c_next)},
+ {"xoff", get_int, offsetof(struct canvas, c_xoff)},
+ {"yoff", get_int, offsetof(struct canvas, c_yoff)},
+ {"xs", get_int, offsetof(struct canvas, c_xs)},
+ {"ys", get_int, offsetof(struct canvas, c_ys)},
+ {"xe", get_int, offsetof(struct canvas, c_xe)},
+ {"ye", get_int, offsetof(struct canvas, c_ye)},
+ {"window", 0, 0, canvas_get_window},
+ {0, 0}
+};
+
+/** }}} */
+
+/** Layout {{{ */
+
+PUSH_TYPE(layout, struct layout)
+CHECK_TYPE(layout, struct layout)
+
+static const struct Xet_reg layout_getters[] = {
+ {0,0}
+};
+
+static int
+get_layout(lua_State *L, void *v)
+{
+ push_layout(L, (struct layout **)v);
+ return 1;
+}
+
+/** }}} */
+
+/** Display {{{ */
+
+PUSH_TYPE(display, struct display)
+
+CHECK_TYPE(display, struct display)
+
+static int
+display_get_canvases(lua_State *L)
+{
+ struct display *d;
+ struct canvas *iter;
+ int count;
+
+ d = check_display(L, 1);
+ lua_newtable(L);
+ for (iter = d->d_cvlist, count = 0; iter; iter = iter->c_next, count++) {
+ lua_pushinteger(L, count);
+ push_canvas(L, &iter);
+ lua_settable(L, -3);
+ }
+
+ return 1;
+}
+
+static const luaL_reg display_methods[] = {
+ {"get_canvases", display_get_canvases},
+ {0, 0}
+};
+
+static int
+display_tostring(lua_State *L)
+{
+ char str[128];
+ struct display *d = check_display(L, 1);
+ snprintf(str, sizeof(str), "{display: tty = '%s', term = '%s'}", d->d_usertty, d->d_termname);
+ lua_pushstring(L, str);
+ return 1;
+}
+
+static const luaL_reg display_metamethods[] = {
+ {"__tostring", display_tostring},
+ {0, 0}
+};
+
+static const struct Xet_reg display_setters[] = {
+ {0, 0}
+};
+
+static const struct Xet_reg display_getters[] = {
+ {"tty", get_string, offsetof(struct display, d_usertty)},
+ {"term", get_string, offsetof(struct display, d_termname)},
+ {"fore", get_window, offsetof(struct display, d_fore)},
+ {"other", get_window, offsetof(struct display, d_other)},
+ {"width", get_int, offsetof(struct display, d_width)},
+ {"height", get_int, offsetof(struct display, d_height)},
+ {"user", get_user, offsetof(struct display, d_user)},
+ {"layout", get_layout, offsetof(struct display, d_layout)},
+ {0, 0}
+};
+
+/** }}} */
+
+/** Screen {{{ */
+
+static int
+screen_get_windows(lua_State *L)
+{
+ struct win *iter;
+ int count;
+
+ lua_newtable(L);
+ for (iter = windows, count = 0; iter; iter = iter->w_next, count++) {
+ lua_pushinteger(L, iter->w_number);
+ push_window(L, &iter);
+ lua_settable(L, -3);
+ }
+
+ return 1;
+}
+
+static int
+screen_get_displays(lua_State *L)
+{
+ struct display *iter;
+ int count;
+
+ lua_newtable(L);
+ for (iter = displays, count = 0; iter; iter = iter->d_next, count++) {
+ lua_pushinteger(L, count);
+ push_display(L, &iter);
+ lua_settable(L, -3);
+ }
+
+ return 1;
+}
+
+static int
+screen_get_display(lua_State *L)
+{
+ push_display(L, &display);
+ return 1;
+}
+
+static int
+screen_exec_command(lua_State *L)
+{
+ const char *command;
+ unsigned int len;
+
+ command = luaL_checklstring(L, 1, &len);
+ if (command)
+ RcLine((char *)command, len);
+
+ return 0;
+}
+
+static int
+screen_append_msg(lua_State *L)
+{
+ const char *msg, *color;
+ int len;
+ msg = luaL_checklstring(L, 1, &len);
+ if (lua_isnil(L, 2))
+ color = NULL;
+ else
+ color = luaL_checklstring(L, 2, &len);
+ AppendWinMsgRend(msg, color);
+ return 0;
+}
+
+static const luaL_reg screen_methods[] = {
+ {"windows", screen_get_windows},
+ {"displays", screen_get_displays},
+ {"display", screen_get_display},
+ {"command", screen_exec_command},
+ {"append_msg", screen_append_msg},
+ {"hook", LuaRegEvent},
+ {"unhook", LuaUnRegEvent},
+ {0, 0}
+};
+
+static const luaL_reg screen_metamethods[] = {
+ {0, 0}
+};
+
+static const struct Xet_reg screen_setters[] = {
+ {0, 0}
+};
+
+static const struct Xet_reg screen_getters[] = {
+ {0, 0}
+};
+
+/** }}} */
+
+/** Public functions {{{ */
+static lua_State *L;
+int LuaInit(void)
+{
+ L = luaL_newstate();
+
+ luaL_openlibs(L);
+
+#define REGISTER(X) struct_register(L, #X , X ## _methods, X##_metamethods, X##_setters, X##_getters)
+
+ REGISTER(screen);
+ REGISTER(window);
+ REGISTER(display);
+ REGISTER(user);
+ REGISTER(canvas);
+
+ return 0;
+}
+
+/* An error message on top of the stack. */
+static void
+LuaShowErr(lua_State *L)
+{
+ struct display *d = display;
+ unsigned int len;
+ const char *message = luaL_checklstring(L, -1, &len);
+ LMsg(0, "%s", message ? message : "Unknown error");
+ lua_pop(L, 1);
+ display = d;
+}
+
+struct fn_def
+{
+ void (*push_fn)(lua_State *, void*);
+ void *value;
+};
+
+static int
+LuaCallProcess(const char *name, struct fn_def defs[])
+{
+ int argc = 0;
+
+ lua_getfield(L, LUA_GLOBALSINDEX, name);
+ if (lua_isnil(L, -1))
+ return 0;
+ for (argc = 0; defs[argc].push_fn; argc++)
+ defs[argc].push_fn(L, defs[argc].value);
+ if (lua_pcall(L, argc, 0, 0) == LUA_ERRRUN && lua_isstring(L, -1))
+ {
+ LuaShowErr(L);
+ return 0;
+ }
+ return 1;
+}
+
+int LuaSource(const char *file, int async)
+{
+ if (!L)
+ return 0;
+ struct stat st;
+ if (stat(file, &st) == -1)
+ Msg(errno, "Error loading lua script file '%s'", file);
+ else
+ {
+ int len = strlen(file);
+ if (len < 4 || strncmp(file + len - 4, ".lua", 4) != 0)
+ return 0;
+ if (luaL_dofile(L, file) && lua_isstring(L, -1))
+ {
+ LuaShowErr(L);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+int LuaFinit(void)
+{
+ if (!L)
+ return 0;
+ lua_close(L);
+ L = (lua_State*)0;
+ return 0;
+}
+
+int
+LuaProcessCaption(const char *caption, struct win *win, int len)
+{
+ if (!L)
+ return 0;
+ struct fn_def params[] = {
+ {lua_pushstring, caption},
+ {push_window, &win},
+ {lua_pushinteger, len},
+ {NULL, NULL}
+ };
+ return LuaCallProcess("process_caption", params);
+}
+
+static void
+push_stringarray(lua_State *L, char **args)
+{
+ int i;
+ lua_newtable(L);
+ for (i = 1; args && *args; i++) {
+ lua_pushinteger(L, i);
+ lua_pushstring(L, *args++);
+ lua_settable(L, -3);
+ }
+}
+
+int
+LuaPushParams(lua_State *L, const char *params, va_list va)
+{
+ int num = 0;
+ while (*params)
+ {
+ switch (*params)
+ {
+ case 's':
+ lua_pushstring(L, va_arg(va, char *));
+ break;
+ case 'S':
+ push_stringarray(L, va_arg(va, char **));
+ break;
+ case 'i':
+ lua_pushinteger(L, va_arg(va, int));
+ break;
+ case 'd':
+ {
+ struct display *d = va_arg(va, struct display *);
+ push_display(L, &d);
+ break;
+ }
+ }
+ params++;
+ num++;
+ }
+ return num;
+}
+
+int LuaCall(char *func, char **argv)
+{
+ int argc;
+ if (!L)
+ return 0;
+
+ StackDump(L, "Before LuaCall\n");
+ lua_getfield(L, LUA_GLOBALSINDEX, func);
+ if (lua_isnil(L, -1))
+ {
+ lua_pushstring(L, "Could not find the script function\n");
+ LuaShowErr(L);
+ return 0;
+ }
+ for (argc = 0; *argv; argv++, argc++)
+ {
+ lua_pushstring(L, *argv);
+ }
+ if (lua_pcall(L, argc, 0, 0) == LUA_ERRRUN)
+ {
+ if(lua_isstring(L, -1))
+ {
+ StackDump(L, "After LuaCall\n");
+ LuaShowErr(L);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int
+LuaDispatch(void *handler, const char *params, va_list va)
+{
+ struct lua_handler *lh = handler;
+ int argc, retvalue;
+
+ if (lh->type == LUA_HANDLER_TYPE_N)
+ lua_getfield(L, LUA_GLOBALSINDEX, lh->u.name);
+ else
+ lua_rawgeti(L, LUA_REGISTRYINDEX, lh->u.reference);
+
+ if (lua_isnil(L, -1))
+ return 0;
+ argc = LuaPushParams(L, params, va);
+
+ if (lua_pcall(L, argc, 1, 0) == LUA_ERRRUN && lua_isstring(L, -1))
+ {
+ StackDump(L, "After LuaDispatch\n");
+ LuaShowErr(L);
+ return 0;
+ }
+ retvalue = lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ return retvalue;
+}
+
+int LuaForeWindowChanged(void)
+{
+ struct fn_def params[] = {
+ {push_display, &display},
+ {push_window, display ? &D_fore : &fore},
+ {NULL, NULL}
+ };
+ if (!L)
+ return 0;
+ return LuaCallProcess("fore_changed", params);
+}
+
+/*
+int
+LuaCommandExecuted(const char *command, const char **args, int argc)
+{
+ if (!L)
+ return 0;
+ struct fn_def params[] = {
+ {lua_pushstring, command},
+ {push_stringarray, args},
+ {NULL, NULL}
+ };
+ return LuaCallProcess("command_executed", params);
+}*/
+
+#define SEVNAME_MAX 30
+static int
+LuaRegEvent(lua_State *L)
+{
+ /* signature: hook(obj, event, handler, priv);
+ * or: hook(event, handler, priv)
+ * returns: A ticket for later unregister. */
+ int idx = 1, argc = lua_gettop(L);
+ int priv = 31; /* Default privilege */
+ struct lua_handler lh;
+
+ char *obj = NULL;
+ const char *objname = "global";
+
+ static char evbuf[SEVNAME_MAX];
+ const char *event;
+
+ struct script_event *sev;
+
+ StackDump(L, "Before RegEvent\n");
+
+ /* Identify the object, if specified */
+ if (luaL_getmetafield(L, 1, "_objname"))
+ {
+ objname = luaL_checkstring(L, -1);
+ lua_pop(L, 1);
+ if (!strcmp("screen", objname))
+ objname = "global";
+ else
+ obj = *(char **)lua_touserdata(L, 1);
+ idx++;
+ }
+
+ event = luaL_checkstring(L, idx++);
+ snprintf(evbuf, SEVNAME_MAX, "%s_%s", objname, event);
+
+ if (lua_isfunction(L, idx))
+ {
+ lua_pushvalue(L, idx);
+ lh.u.reference = luaL_ref(L, LUA_REGISTRYINDEX);
+ lh.type = LUA_HANDLER_TYPE_F;
+ idx++;
+ }
+ else
+ {
+ lh.type = LUA_HANDLER_TYPE_N;
+ lh.u.name = SaveStr(luaL_checkstring(L, idx++));
+ }
+
+ StackDump(L, "In RegEvent\n");
+
+ if (idx <= argc)
+ priv = luaL_checkinteger(L, idx);
+
+ sev = object_get_event(obj, evbuf);
+ if (sev)
+ {
+ struct listener *l;
+ l = (struct listener *)malloc(sizeof(struct listener));
+ if (!l)
+ return luaL_error(L, "Out of memory");
+ l->handler = &lh;
+ l->priv = priv;
+ l->dispatcher = LuaDispatch;
+ if (register_listener(sev, l))
+ {
+ free(l);
+ if (lh.type == LUA_HANDLER_TYPE_N)
+ return luaL_error(L, "Handler %s has already been registered", lh.u.name);
+ else
+ return luaL_error(L, "Handler has already been registered");
+ }
+ /* Return the handler for un-register */
+ l->handler = malloc(sizeof(struct lua_handler));
+ memcpy(l->handler, &lh, sizeof(struct lua_handler));
+ lua_pushlightuserdata(L, l);
+ }
+ else
+ return luaL_error(L, "Invalid event specified: %s for object %s", event, objname);
+
+ StackDump(L, "After RegEvent\n");
+ return 1;
+}
+
+static int
+LuaUnRegEvent(lua_State *L)
+{
+ /* signature: unhook([obj], ticket)
+ * returns: true of success, false otherwise */
+ int idx = 1;
+ struct listener *l;
+
+ /* If the param is not as expected */
+ if (!lua_islightuserdata(L, idx))
+ {
+ /* Then it should be the userdata to be ignore, but if not ... */
+ if (!lua_isuserdata(L, idx))
+ luaL_checktype(L, idx, LUA_TLIGHTUSERDATA);
+ idx++;
+ luaL_checktype(L, idx, LUA_TLIGHTUSERDATA);
+ }
+
+ l = (struct listener*)lua_touserdata(L, idx++);
+
+ /* Validate the listener structure */
+ if (!l || !l->handler)
+ {
+ /* invalid */
+ lua_pushboolean(L,0);
+ }
+ else
+ {
+ struct lua_handler *lh = l->handler;
+ if (lh->type == LUA_HANDLER_TYPE_N)
+ Free(lh->u.name);
+ Free(l->handler);
+ unregister_listener(l);
+ lua_pushboolean(L, 1);
+ }
+
+ return 1;
+}
+
+/** }}} */
+
+struct ScriptFuncs LuaFuncs =
+{
+ LuaForeWindowChanged,
+ LuaProcessCaption
+};
+
+struct binding lua_binding =
+{
+ "lua", /*name*/
+ 0, /*inited*/
+ 0, /*registered*/
+ LuaInit,
+ LuaFinit,
+ LuaCall,
+ LuaSource,
+ 0, /*b_next*/
+ &LuaFuncs
+};
+
diff --git a/src/process.c b/src/process.c
index da9188d..6c8cba7 100644
--- a/src/process.c
+++ b/src/process.c
@@ -171,6 +171,12 @@ static struct action *FindKtab __P((char *, int));
static void SelectFin __P((char *, int, char *));
static void SelectLayoutFin __P((char *, int, char *));
+/* Alias */
+static void AddAlias __P((const char *name, const char *val , char **args, int *argl, int count));
+static struct alias * FindAlias __P((const char *name));
+static struct alias * FindAliasnr __P((int));
+static void DelAlias __P((const char *name));
+static int DoAlias __P((const char *, char **, int *));
extern struct layer *flayer;
extern struct display *display, *displays;
@@ -248,6 +254,20 @@ struct digraph
int value;
};
+/*
+ * Command aliases.
+ */
+struct alias {
+ int nr;
+ char *name; /* Name of the alias */
+ int cmdnr; /* Number of the command this is alias for */
+ char **args; /* The argument list for the command */
+ int *argl;
+ struct alias *next;
+};
+struct alias *g_aliases_list = NULL;
+
+
/* digraph table taken from old vim and rfc1345 */
static struct digraph digraphs[MAX_DIGRAPH + 1] = {
{' ', ' ', 160}, /*   */
@@ -1156,11 +1176,24 @@ int key;
struct acluser *user;
user = display ? D_user : users;
+
+ if (nr > RC_LAST)
+ {
+ struct alias *alias = FindAliasnr(nr);
+ if (alias)
+ {
+ DoAlias(alias->name, act->args, act->argl);
+ return;
+ }
+ nr = RC_ILLEGAL;
+ }
+
if (nr == RC_ILLEGAL)
{
debug1("key '%c': No action\n", key);
return;
}
+
n = comms[nr].flags;
/* Commands will have a CAN_QUERY flag, depending on whether they have
* something to return on a query. For example, 'windows' can return a result,
@@ -2645,6 +2678,12 @@ int key;
break;
case RC_SLEEP:
break; /* Already handled */
+ case RC_ALIAS:
+ if (argc == 1)
+ DelAlias(args[0]);
+ else
+ AddAlias(args[0], args[1], args+1, argl+1, argc - 1);
+ break;
case RC_TERM:
s = NULL;
if (ParseSaveStr(act, &s))
@@ -3282,10 +3321,15 @@ int key;
{
if ((i = FindCommnr(args[1])) == RC_ILLEGAL)
{
- OutputMsg(0, "%s: bind: unknown command '%s'", rc_name, args[1]);
- break;
+ struct alias *alias = FindAlias(args[1]);
+ if (!alias)
+ {
+ OutputMsg(0, "%s: bind: unknown command or alias '%s'", rc_name, args[1]);
+ break;
+ }
+ i = alias->nr;
}
- if (CheckArgNum(i, args + 2) < 0)
+ if (i <= RC_LAST && CheckArgNum(i, args + 2) < 0)
break;
ClearAction(&ktabp[n]);
SaveAction(ktabp + n, i, args + 2, argl + 2);
@@ -3413,10 +3457,15 @@ int key;
{
if ((newnr = FindCommnr(args[1])) == RC_ILLEGAL)
{
- OutputMsg(0, "%s: bindkey: unknown command '%s'", rc_name, args[1]);
- break;
+ struct alias *alias = FindAlias(args[1]);
+ if (!alias)
+ {
+ OutputMsg(0, "%s: bindkey: unknown command or alias '%s'", rc_name, args[1]);
+ break;
+ }
+ newnr = alias->nr;
}
- if (CheckArgNum(newnr, args + 2) < 0)
+ if (newnr <= RC_LAST && CheckArgNum(newnr, args + 2) < 0)
break;
ClearAction(newact);
SaveAction(newact, newnr, args + 2, argl + 2);
@@ -3953,9 +4002,15 @@ int key;
#endif
break;
- case RC_SOURCE:
- do_source(*args);
- break;
+ case RC_SOURCE:
+ do_source(*args);
+ break;
+
+#ifdef SCRIPT
+ case RC_SCRIPT:
+ ScriptCmd(argc, (const char **) args);
+ break;
+#endif
#ifdef MULTIUSER
case RC_SU:
@@ -4177,10 +4232,15 @@ int key;
{
if ((i = FindCommnr(args[1])) == RC_ILLEGAL)
{
- OutputMsg(0, "%s: idle: unknown command '%s'", rc_name, args[1]);
- break;
+ struct alias *alias = FindAlias(args[1]);
+ if (!alias)
+ {
+ OutputMsg(0, "%s: idle: unknown command or alias '%s'", rc_name, args[1]);
+ break;
+ }
+ i = alias->nr;
}
- if (CheckArgNum(i, args + 2) < 0)
+ if (i <= RC_LAST && CheckArgNum(i, args + 2) < 0)
break;
ClearAction(&idleaction);
SaveAction(&idleaction, i, args + 2, argl + 2);
@@ -4461,6 +4521,12 @@ int key;
#endif
break;
}
+
+#ifdef SCRIPT
+ if (nr < RC_LAST)
+ trigger_sevent(&globalevents.cmdexecuted, comms[nr].name, args);
+#endif
+
if (display != odisplay)
{
for (display = displays; display; display = display->d_next)
@@ -4470,14 +4536,66 @@ int key;
}
#undef OutputMsg
+static int
+DoAlias(name, args, argl)
+const char *name;
+char **args;
+int *argl;
+{
+ char **mergeds;
+ int *mergedl;
+ int count, i;
+ struct alias *alias = FindAlias(name);
+
+ if (alias == NULL)
+ return 0;
+
+ count = 0;
+ for (; args && args[count]; count++)
+ ;
+ for (i = 0; alias->args && alias->args[i]; i++, count++)
+ ;
+ ++count;
+
+ if ((mergeds = malloc(count * sizeof(char *))) == 0)
+ return 0;
+ if ((mergedl = malloc(count * sizeof(int))) == 0)
+ {
+ free(mergeds);
+ return 0;
+ }
+ for (count = 0; alias->args && alias->args[count]; count++)
+ {
+ mergeds[count] = alias->args[count];
+ mergedl[count] = alias->argl[count];
+ }
+ for (i = 0; args && args[i]; i++, count++)
+ {
+ mergeds[count] = args[i];
+ mergedl[count] = argl[i];
+ }
+ mergeds[count] = 0;
+ mergedl[count] = 0 ;
+
+ DoCommand(mergeds, mergedl);
+
+ free(mergeds);
+ free(mergedl);
+ return 1;
+}
+
void
-DoCommand(argv, argl)
+DoCommand(argv, argl)
char **argv;
int *argl;
{
struct action act;
const char *cmd = *argv;
+ /* Alias? */
+ if (DoAlias(*argv, argv + 1, argl + 1))
+ return;
+
act.quiet = 0;
/* For now, we actually treat both 'supress error' and 'suppress normal message' as the
* same, and ignore all messages on either flag. If we wanted to do otherwise, we would
@@ -5133,6 +5251,9 @@ struct win *wi;
if (wi)
WindowChanged(wi, 'u');
flayer = D_forecv->c_layer;
+#ifdef SCRIPT
+ ScriptForeWindowChanged();
+#endif
/* Activate called afterwards, so no RefreshHStatus needed */
}
@@ -6535,6 +6656,141 @@ char *presel;
return wi;
}
+/**
+ * Add an alias
+ */
+void
+AddAlias(name, value, args, argl, count)
+const char *name;
+const char *value;
+char **args;
+int *argl;
+int count;
+{
+ struct alias *nalias = NULL;
+ static next_command = RC_LAST;
+ int nr;
+
+ /* Make sure we don't already have this alias name defined. */
+ if (FindAlias(name) != NULL)
+ {
+ Msg(0, "alias already defined: %s", name);
+ return;
+ }
+
+ /* Make sure the alias maps to something */
+ if ((nr = FindCommnr((char *)value)) == RC_ILLEGAL)
+ {
+ struct alias *alias = FindAlias(value);
+ if (!alias)
+ {
+ Msg(0, "%s: could not find command or alias '%s'", rc_name, value);
+ return;
+ }
+ nr = alias->nr;
+ }
+
+ nalias = (struct alias *)calloc(1, sizeof(struct alias));
+
+ /* store it */
+ nalias->next = NULL;
+ nalias->name = SaveStr(name);
+ nalias->cmdnr = nr;
+ if (count > 0)
+ {
+ nalias->args = SaveArgs(args);
+ nalias->argl = calloc(count + 1, sizeof(int));
+ while (count--)
+ nalias->argl[count] = argl[count];
+ }
+ nalias->nr = ++next_command;
+
+ /* Add to head */
+ nalias->next = g_aliases_list;
+ g_aliases_list = nalias;
+}
+
+
+/**
+ * Find an alias by name.
+ */
+static struct alias *
+FindAlias(name)
+const char *name;
+{
+ struct alias *t = g_aliases_list;
+
+ while(t != NULL)
+ {
+ if ((t->name != NULL) &&
+ (strcmp(t->name, name) == 0))
+ return t;
+
+ t = t->next;
+ }
+
+ return NULL;
+}
+
+/**
+ * Find an alias by number.
+ */
+static struct alias *
+FindAliasnr(nr)
+int nr;
+{
+ struct alias *t;
+ for (t = g_aliases_list; t; t = t->next)
+ {
+ if (t->nr == nr)
+ return t;
+ }
+
+ return NULL;
+}
+
+/**
+ * Delete an alias
+ */
+void
+DelAlias(name)
+const char *name;
+{
+ /* Find the previous alias */
+ struct alias *cur = g_aliases_list;
+ struct alias **pcur = &g_aliases_list;
+
+ while (cur != NULL)
+ {
+ if ((cur->name != NULL) &&
+ (strcmp(cur->name, name) == 0))
+ {
+ struct alias *found = cur;
+ int c;
+
+ /* remove this one from the chain. */
+ *pcur = found->next;
+
+ free(found->name);
+ if (found->args)
+ {
+ for (c = 0; found->args[c]; c++)
+ free(found->args[c]);
+ free(found->args);
+ free(found->argl);
+ }
+ free(found);
+
+ Msg(0, "alias %s removed", name);
+ return;
+ }
+ pcur = &cur->next;
+ cur = cur->next;
+ }
+
+ Msg(0, "alias %s not found", name);
+}
+
#if 0
/* sorted list of all commands */
diff --git a/src/screen.c b/src/screen.c
index 3dde3b4..ba5c259 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -388,6 +388,11 @@ char **av;
#ifdef MULTIUSER
char *sockp;
#endif
+
+#ifdef SCRIPT
+ char *script_file = 0;
+#endif
+
char *sty = 0;
#if (defined(AUX) || defined(_AUX_SOURCE)) && defined(POSIX)
@@ -754,6 +759,13 @@ char **av;
nwin_options.encoding = nwin_options.encoding == -1 ? UTF8 : 0;
break;
#endif
+ case 'u':
+ if (--ac == 0)
+ exit_with_usage(myname, "Specify lua script file with -u", NULL);
+ if (script_file)
+ free(script_file);
+ script_file = SaveStr(*++av);
+ break;
default:
exit_with_usage(myname, "Unknown option %s", --ap);
}
@@ -1345,6 +1357,17 @@ char **av;
ServerSocket = MakeServerSocket();
InitKeytab();
+
+#ifdef SCRIPT
+ LoadBindings();
+ if (script_file)
+ {
+ ScriptSource(script_file);
+ free(script_file);
+ script_file = 0;
+ }
+#endif
+
#ifdef ETCSCREENRC
# ifdef ALLOW_SYSSCREENRC
if ((ap = getenv("SYSSCREENRC")))
@@ -1803,6 +1826,11 @@ int i;
signal(SIGCHLD, SIG_DFL);
signal(SIGHUP, SIG_IGN);
debug1("Finit(%d);\n", i);
+
+#ifdef SCRIPT
+ FinalizeBindings();
+#endif
+
while (windows)
{
struct win *p = windows;
@@ -1926,6 +1954,7 @@ int mode;
case D_DETACH:
AddStrSock("detached");
sign = SIG_BYE;
+ trigger_sevent(&globalevents.detached, display, 0x0);
break;
#ifdef BSDJOBS
case D_STOP:
@@ -1936,6 +1965,7 @@ int mode;
case D_REMOTE:
AddStrSock("remote detached");
sign = SIG_BYE;
+ trigger_sevent(&globalevents.detached, display, 0x1);
break;
#endif
#ifdef POW_DETACH
@@ -1947,6 +1977,7 @@ int mode;
AddStr("\r\n");
}
sign = SIG_POWER_BYE;
+ trigger_sevent(&globalevents.detached, display, 0x2);
break;
#ifdef REMOTE_DETACH
case D_REMOTE_POWER:
@@ -1957,6 +1988,7 @@ int mode;
AddStr("\r\n");
}
sign = SIG_POWER_BYE;
+ trigger_sevent(&globalevents.detached, display, 0x1 | 0x2); /* Remote and power */
break;
#endif
#endif
@@ -2473,6 +2505,30 @@ time_t now;
return bt->result;
}
+void
+AppendWinMsgRend(str, color)
+char *str, *color;
+{
+ char *p;
+ int r = -1;
+ if (winmsg_numrend >= MAX_WINMSG_REND)
+ return;
+ p = winmsg_buf + strlen(winmsg_buf);
+ if (color)
+ {
+ if (*color != '-')
+ {
+ r = ParseAttrColor(color, 0, 0);
+ if (r == -1)
+ return;
+ }
+ winmsg_rend[winmsg_numrend] = r;
+ winmsg_rendpos[winmsg_numrend] = p - winmsg_buf;
+ winmsg_numrend++;
+ }
+ strncpy(p, str, winmsg_buf + sizeof(winmsg_buf) - p);
+}
+
int
AddWinMsgRend(str, r)
const char *str;
@@ -2518,17 +2574,25 @@ int rec;
int truncper = 0;
int trunclong = 0;
struct backtick *bt;
-
+
if (winmsg_numrend >= 0)
winmsg_numrend = 0;
else
winmsg_numrend = -winmsg_numrend;
-
+
+ *p = '\0';
+#ifdef SCRIPT
+ if (ScriptProcessCaption(str, win, padlen))
+ return winmsg_buf;
+#endif
+ if (!display)
+ return winmsg_buf;
+
tick = 0;
tm = 0;
ctrl = 0;
gettimeofday(&now, NULL);
- for (; *s && (l = winmsg_buf + MAXSTR - 1 - p) > 0; s++, p++)
+ for (s = str; *s && (l = winmsg_buf + MAXSTR - 1 - p) > 0; s++, p++)
{
*p = *s;
if (ctrl)
@@ -3076,7 +3140,7 @@ int start, max;
if (start-- > 0)
s++;
else
- PUTCHARLP(*s++);
+ PUTCHARLP(*s++);
}
}
r = winmsg_rend[i];
diff --git a/src/screen.h b/src/screen.h
index 5c93f32..ff361df 100644
--- a/src/screen.h
+++ b/src/screen.h
@@ -48,6 +48,7 @@
#include "comm.h"
#include "layer.h"
#include "term.h"
+#include "script.h"
#ifdef DEBUG
diff --git a/src/script.c b/src/script.c
new file mode 100644
index 0000000..2b09a9c
--- /dev/null
+++ b/src/script.c
@@ -0,0 +1,290 @@
+/* Copyright (c) 2008 Sadrul Habib Chowdhury (sadrul@users.sf.net)
+ * 2009 Rui Guo (firemeteor.guo@gmail.com)
+ *
+ * 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 "config.h"
+#include "screen.h"
+#include <stddef.h>
+
+/*Binding structure & functions*/
+
+struct binding *bindings = NULL;
+
+static void
+register_binding (struct binding *new_binding)
+{
+ if (!new_binding->registered)
+ {
+ new_binding->b_next = bindings;
+ bindings = new_binding;
+ new_binding->registered = 1;
+ }
+}
+
+#ifdef LUA_BINDING
+extern struct binding lua_binding;
+#endif
+
+void LoadBindings(void)
+{
+#ifdef LUA_BINDING
+ register_binding(&lua_binding);
+#endif
+}
+
+void
+FinalizeBindings (void)
+{
+ struct binding *binding=bindings;
+ while(binding)
+ {
+ if (binding->inited)
+ binding->bd_Finit();
+ binding = binding->b_next;
+ }
+}
+
+void
+ScriptSource(int argc, const char **argv)
+{
+ int ret = 0;
+ int async = 0;
+ const char *bd_select = 0, *script;
+ struct binding *binding = bindings;
+
+ /* Parse the commandline options
+ * sourcescript [-async|-a] [-binding|-b <binding>] script
+ */
+ while (*argv && **argv == '-')
+ {
+ /* check for (-a | -async) */
+ const char *arg = *argv;
+ if ((arg[1] == 'a' && !arg[2])
+ || strcmp(arg, "-async") == 0)
+ async = 1;
+ /* check for (-b | -binding) */
+ else if ((arg[1] == 'b' && !arg[2])
+ || strcmp(arg, "-binding") == 0) {
+ argv++;
+ bd_select = *argv;
+ }
+ argv++;
+ }
+ script = *argv;
+
+ while (binding) {
+ if (!bd_select || strcmp(bd_select, binding->name) == 0) {
+ /*dynamically initialize the binding*/
+ if (!binding->inited)
+ {
+ binding->bd_Init();
+ binding->inited = 1;
+ }
+
+ /*and source the script*/
+ if (ret = binding->bd_Source(script, async))
+ break;
+ }
+ binding = binding->b_next;
+ }
+ if (!ret)
+ LMsg(1, "Could not source specified script %s", script);
+}
+
+int
+ScriptCall(const char *func, const char **argv)
+{
+ /*TODO*/
+ return LuaCall(func, argv);
+}
+
+void
+ScriptCmd(int argc, const char **argv)
+{
+ const char * sub = *argv;
+ argv++;argc--;
+ if (!strcmp(sub, "call"))
+ ScriptCall(*argv, argv+1);
+ else if (!strcmp(sub, "source"))
+ ScriptSource(argc, argv);
+}
+
+/* Event notification handling */
+
+struct gevents globalevents;
+
+/* To add a new event, introduce a field for that event to the object in
+ * question, and don't forget to put an descriptor here. NOTE: keep the
+ * name field sorted in alphabet order, the searching relies on it.
+ *
+ * the params string specifies the expected parameters. The length of the
+ * string equals to the number of parameters. Each char specifies the type of
+ * the parameter, with its meaning similar to those in printf().
+ *
+ * s: string (char *)
+ * S: string array (char **)
+ * i: signed int
+ * d: display
+ */
+
+struct sev_description {
+ char *name;
+ char *params;
+ int offset;
+} event_table[] = {
+ /* Global events */
+ {"global_cmdexecuted", "sS", offsetof(struct gevents, cmdexecuted)},
+ {"global_detached", "di", offsetof(struct gevents, detached)},
+ /* The command "detach" triggers both 'cmdexecuted' and 'detached' events.
+ However, we need the 'detached' event to trigger callbacks from remote detaches.
+ */
+
+ /* Window events */
+ {"window_resize", "", offsetof(struct win, w_sev.resize)},
+ {"window_can_resize", "", offsetof(struct win, w_sev.canresize)}
+};
+
+/* Get the event queue with the given name in the obj. If the obj is NULL,
+ * global events are searched. If no event is found, a NULL is returned.
+ */
+struct script_event *
+object_get_event(char *obj, const char *name)
+{
+ int lo, hi, n, cmp;
+ if (!obj)
+ obj = (char *)&globalevents;
+
+ lo = 0;
+ n = hi = sizeof(event_table) / sizeof(struct sev_description);
+ while (lo < hi)
+ {
+ int half;
+ half = (lo + hi) >> 1;
+ cmp = strcmp(name, event_table[half].name);
+ if (cmp > 0)
+ lo = half + 1;
+ else
+ hi = half;
+ }
+
+ if (lo >= n || strcmp(name, event_table[lo].name))
+ return 0;
+ else
+ {
+ /*found an entry.*/
+ struct script_event *res;
+ res = (struct script_event *) (obj + event_table[lo].offset);
+ /*Setup the parameter record.*/
+ res->params = event_table[lo].params;
+ return res;
+ }
+}
+
+/* Put a listener in a proper position in the chain
+ * according to the privlege.
+ * Not insert duplicate entry. return zero if successful.*/
+int
+register_listener(struct script_event *ev, struct listener *l)
+{
+ unsigned int priv = l->priv;
+ struct listener *p, *iter = &ev->listeners;
+
+#if 0
+ while(iter->chain && priv >= iter->chain->priv)
+ {
+ iter = iter->chain;
+ /* return if duplicate found*/
+ if (iter->handler == l->handler
+ && iter->dispatcher == l->dispatcher)
+ return 1;
+ }
+#endif
+ p = iter;
+
+ l->chain = p->chain;
+ l->prev = p;
+ if (p->chain)
+ p->chain->prev = l;
+ p->chain = l;
+ return 0;
+}
+
+void
+unregister_listener(struct listener *l)
+{
+ struct listener *p = l->prev;
+ p->chain = l->chain;
+ if (l->chain)
+ l->chain->prev = p;
+ l->chain = l->prev = 0;
+ l->handler = 0;
+ free(l);
+}
+
+/* Trigger event with given parameters.*/
+int
+trigger_sevent(struct script_event *ev, VA_DOTS)
+{
+ int res = 0;
+ struct listener *chain;
+ char *params;
+ VA_LIST(va);
+ /*invalid or un-registered event structure*/
+ if (!ev || !ev->params)
+ return 0;
+
+ /*process the chain in order, stop if any of the handler returns true.*/
+ chain = ev->listeners.chain;
+ params = ev->params;
+ while (chain)
+ {
+ VA_START(va, ev);
+ res = chain->dispatcher(chain->handler, params, va);
+ VA_END(va);
+ if (res)
+ break;
+ chain = chain->chain;
+ }
+
+ return res;
+}
+
+#define ALL_SCRIPTS(fn, params, stop) do { \
+ struct binding *iter; \
+ for (iter = bindings; iter; iter = iter->b_next) \
+ { \
+ if (iter->fns && iter->fns->fn && (ret = (iter->fns->fn params)) && stop) \
+ break; \
+ } \
+} while (0)
+
+void ScriptForeWindowChanged(void)
+{
+ int ret;
+ ALL_SCRIPTS(sf_ForeWindowChanged, (), 0);
+}
+
+int ScriptProcessCaption(const char *str, struct win *win, int len)
+{
+ int ret = 0;
+ ALL_SCRIPTS(sf_ProcessCaption, (str, win, len), 1);
+ return ret;
+}
+
diff --git a/src/script.h b/src/script.h
new file mode 100644
index 0000000..c268eb5
--- /dev/null
+++ b/src/script.h
@@ -0,0 +1,89 @@
+/* Copyright (c) 2008 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
+ *
+ ****************************************************************
+ * $Id$ FAU
+ */
+#ifndef SCRIPT_H
+#define SCRIPT_H
+struct win;
+
+/*Obsolete*/
+struct ScriptFuncs
+{
+ int (*sf_ForeWindowChanged) __P((void));
+ int (*sf_ProcessCaption) __P((const char *, struct win *, int len));
+};
+
+/***Language binding***/
+struct binding
+{
+ char * name;
+ int inited;
+ int registered;
+ int (*bd_Init) __P((void));
+ int (*bd_Finit) __P((void));
+ int (*bd_call) __P((char *func, char **argv));
+ /*Returns zero on failure, non zero on success*/
+ int (*bd_Source) __P((const char *, int));
+ struct binding *b_next;
+ struct ScriptFuncs *fns;
+};
+
+void LoadBindings(void);
+void FinializeBindings(void);
+void ScriptCmd __P((int argc, const char **argv));
+
+/***Script events***/
+
+struct script_event;
+/* Script event listener */
+struct listener
+{
+ /*Binding dependent event handler data*/
+ void *handler;
+
+ /* dispatcher provided by the binding.
+ * The return value is significant:
+ * a non-zero value will stop further
+ * notification to the rest of the chain.*/
+ int (*dispatcher) __P((void *handler, const char *params, va_list va));
+
+ /* smaller means higher privilege.*/
+ unsigned int priv;
+ struct listener *chain;
+ struct listener *prev;
+};
+
+/* the script_event structure needs to be zeroed before using.
+ * embeding this structure directly into screen objects will do the job, as
+ * long as the objects are created from calloc() call.*/
+struct script_event
+{
+ /* expected parameter description of this event. */
+ char *params;
+ struct listener listeners;
+};
+struct script_event* object_get_event __P((char *obj, const char *name));
+int trigger_sevent(struct script_event *ev, VA_DOTS);
+
+struct gevents {
+ struct script_event cmdexecuted;
+ struct script_event detached;
+};
+extern struct gevents globalevents;
+#endif
diff --git a/src/scripts/cmdcallback.lua b/src/scripts/cmdcallback.lua
new file mode 100644
index 0000000..54fa4eb
--- /dev/null
+++ b/src/scripts/cmdcallback.lua
@@ -0,0 +1,44 @@
+--[[ For now, this sample function will simply record all the commands executed ]]--
+
+ticket1 = screen.hook("cmdexecuted", function(name, args)
+ os.execute('mkdir -p /tmp/debug')
+ local f = io.open('/tmp/debug/22', 'a')
+ f:write("Command executed: " .. name)
+
+ for i, c in pairs(args) do
+ f:write(" " .. c)
+ end
+
+ f:write("\n")
+ f:close()
+ return 0
+ end)
+
+function cmd(name, args)
+ os.execute('mkdir -p /tmp/debug')
+ local f = io.open('/tmp/debug/11', 'a')
+ f:write("Command executed: " .. name)
+
+ for i, c in pairs(args) do
+ f:write(" " .. c)
+ end
+
+ f:write("\n")
+ f:close()
+ return 0
+end
+
+ticket2 = screen.hook("cmdexecuted", "cmd")
+
+function unhook()
+ if ticket1 ~= nil then
+ screen.unhook(ticket1)
+ ticket1 = nil
+ end
+
+ if ticket2 ~= nil then
+ screen.unhook(ticket2)
+ ticket2 = nil
+ end
+end
+
diff --git a/src/scripts/findwindow.lua b/src/scripts/findwindow.lua
new file mode 100644
index 0000000..ff393df
--- /dev/null
+++ b/src/scripts/findwindow.lua
@@ -0,0 +1,19 @@
+function find_window(name)
+ display = screen.display()
+ canvases = display:get_canvases()
+ for i, c in pairs(canvases) do
+ w = c.window
+ if w ~= nil and (w.title == name or tostring(w.number) == name) then c:select() return end
+ end
+
+ -- Try partial matches, just like 'select'
+ for i, c in pairs(canvases) do
+ w = c.window
+ if w ~= nil and w.title:sub(1, name:len()) == name then c:select() return end
+ end
+
+ -- We didn't find the desired window in any canvas
+ -- So switch to the window in the current canvas instead
+ screen.command("select " .. name)
+end
+
diff --git a/src/window.h b/src/window.h
index 1f70f39..e5b0d75 100644
--- a/src/window.h
+++ b/src/window.h
@@ -297,6 +297,13 @@ struct win
#else
int w_exitstatus;
#endif
+#ifdef SCRIPT
+ struct
+ {
+ struct script_event resize;
+ struct script_event canresize;
+ } w_sev; /*For Script events. */
+#endif
};