about summary refs log tree commit diff stats
path: root/src/lcurses
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2021-11-19 19:11:03 -0800
committerKartik K. Agaram <vc@akkartik.com>2021-11-19 19:11:03 -0800
commitb43747ecef13a7cbe5eb5842d03d803165a473b3 (patch)
treedbecfd6324961dd4c1debfc3fb1128acf5794a55 /src/lcurses
parent9837d327f5617852167d82ed589c9342539072ac (diff)
downloadteliva-b43747ecef13a7cbe5eb5842d03d803165a473b3.tar.gz
inline lcurses maximally rather than minimally
Until now we had just the bare minimum bindings needed for the demos
built so far. Now we have all of lcurses building in place with minimal
changes.

The changes in this commit can run hanoi.lua when inlined into Lua 5.1,
but don't work with Teliva.
Diffstat (limited to 'src/lcurses')
-rw-r--r--src/lcurses/_helpers.c210
-rw-r--r--src/lcurses/chstr.c293
-rw-r--r--src/lcurses/compat-5.2.c682
-rw-r--r--src/lcurses/compat-5.2.h168
-rw-r--r--src/lcurses/curses.c1555
-rw-r--r--src/lcurses/curses.lua58
-rw-r--r--src/lcurses/strlcpy.c76
-rw-r--r--src/lcurses/window.c1940
8 files changed, 4982 insertions, 0 deletions
diff --git a/src/lcurses/_helpers.c b/src/lcurses/_helpers.c
new file mode 100644
index 0000000..d229d15
--- /dev/null
+++ b/src/lcurses/_helpers.c
@@ -0,0 +1,210 @@
+/*
+ * POSIX library for Lua 5.1, 5.2 & 5.3.
+ * (c) Gary V. Vaughan <gary@vaughan.pe>, 2013-2017
+ * (c) Reuben Thomas <rrt@sc3d.org> 2010-2013
+ * (c) Natanael Copa <natanael.copa@gmail.com> 2008-2010
+ * Clean up and bug fixes by Leo Razoumov <slonik.az@gmail.com> 2006-10-11
+ * Luiz Henrique de Figueiredo <lhf@tecgraf.puc-rio.br> 07 Apr 2006 23:17:49
+ * Based on original by Claudio Terra for Lua 3.x.
+ * With contributions by Roberto Ierusalimschy.
+ * With documentation from Steve Donovan 2012
+ */
+
+#ifndef LCURSES__HELPERS_C
+#define LCURSES__HELPERS_C 1
+
+#include <errno.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>		/* for _POSIX_VERSION */
+
+#include <ncurses.h>
+#include <term.h>
+
+/* NetBSD's default curses implementation is not quite complete.  This
+   disables those missing functions unless linked to ncurses instead. */
+#if defined NCURSES_VERSION || !defined __NetBSD__
+#  define LCURSES_POSIX_COMPLIANT 1
+#endif
+
+#include "../lua.h"
+#include "../lualib.h"
+#include "../lauxlib.h"
+
+#if LUA_VERSION_NUM < 503
+#  define lua_isinteger lua_isnumber
+#  if LUA_VERSION_NUM == 501
+#    include "compat-5.2.c"
+#  endif
+#endif
+
+#if LUA_VERSION_NUM == 502 || LUA_VERSION_NUM == 503
+#  define lua_objlen lua_rawlen
+#  define lua_strlen lua_rawlen
+#  define luaL_openlib(L,n,l,nup) luaL_setfuncs((L),(l),(nup))
+#  define luaL_register(L,n,l) (luaL_newlib(L,l))
+#endif
+
+#ifndef STREQ
+#  define STREQ(a, b)     (strcmp (a, b) == 0)
+#endif
+
+/* Mark unused parameters required only to match a function type
+   specification. */
+#ifdef __GNUC__
+#  define LCURSES_UNUSED(x) UNUSED_ ## x __attribute__((__unused__))
+#else
+#  define LCURSES_UNUSED(x) UNUSED_ ## x
+#endif
+
+/* LCURSES_STMT_BEG/END are used to create macros that expand to a
+   single compound statement in a portable way. */
+#if defined __GNUC__ && !defined __STRICT_ANSI__ && !defined __cplusplus
+#  define LCURSES_STMT_BEG	(void)(
+#  define LCURSES_STMT_END	)
+#else
+#  if (defined sun || defined __sun__)
+#    define LCURSES_STMT_BEG	if (1)
+#    define LCURSES_STMT_END	else (void)0
+#  else
+#    define LCURSES_STMT_BEG	do
+#    define LCURSES_STMT_END	while (0)
+#  endif
+#endif
+
+
+/* The extra indirection to these macros is required so that if the
+   arguments are themselves macros, they will get expanded too.  */
+#define LCURSES__SPLICE(_s, _t)	_s##_t
+#define LCURSES_SPLICE(_s, _t)	LCURSES__SPLICE(_s, _t)
+
+#define LCURSES__STR(_s)	#_s
+#define LCURSES_STR(_s)		LCURSES__STR(_s)
+
+/* The +1 is to step over the leading '_' that is required to prevent
+   premature expansion of MENTRY arguments if we didn't add it.  */
+#define LCURSES__STR_1(_s)	(#_s + 1)
+#define LCURSES_STR_1(_s)	LCURSES__STR_1(_s)
+
+#define LCURSES_CONST(_f)	LCURSES_STMT_BEG {			\
+					lua_pushinteger(L, _f);		\
+					lua_setfield(L, -2, #_f);	\
+				} LCURSES_STMT_END
+
+#define LCURSES_FUNC(_s)	{LCURSES_STR_1(_s), (_s)}
+
+#define pushokresult(b)	pushboolresult((int) (b) == OK)
+
+#ifndef errno
+extern int errno;
+#endif
+
+
+/* ========================= *
+ * Bad argument diagnostics. *
+ * ========================= */
+
+
+static int
+argtypeerror(lua_State *L, int narg, const char *expected)
+{
+	const char *got = luaL_typename(L, narg);
+	return luaL_argerror(L, narg,
+		lua_pushfstring(L, "%s expected, got %s", expected, got));
+}
+
+static lua_Integer
+checkinteger(lua_State *L, int narg, const char *expected)
+{
+	lua_Integer d = lua_tointeger(L, narg);
+	if (d == 0 && !lua_isinteger(L, narg))
+		argtypeerror(L, narg, expected);
+	return d;
+}
+
+static int
+checkint(lua_State *L, int narg)
+{
+	return (int)checkinteger(L, narg, "int");
+}
+
+
+static chtype
+checkch(lua_State *L, int narg)
+{
+	if (lua_isnumber(L, narg))
+		return (chtype)checkint(L, narg);
+	if (lua_isstring(L, narg))
+		return *lua_tostring(L, narg);
+
+	return argtypeerror(L, narg, "int or char");
+}
+
+
+static chtype
+optch(lua_State *L, int narg, chtype def)
+{
+	if (lua_isnoneornil(L, narg))
+		return def;
+	if (lua_isnumber(L, narg) || lua_isstring(L, narg))
+		return checkch(L, narg);
+	return argtypeerror(L, narg, "int or char or nil");
+}
+
+
+static int
+optint(lua_State *L, int narg, lua_Integer def)
+{
+	if (lua_isnoneornil(L, narg))
+		return (int) def;
+	return (int)checkinteger(L, narg, "int or nil");
+}
+
+#define pushboolresult(b)	(lua_pushboolean(L, (b)), 1)
+
+#define pushintresult(n)	(lua_pushinteger(L, (n)), 1)
+
+#define pushstringresult(s)	(lua_pushstring(L, (s)), 1)
+
+
+
+/* ================== *
+ * Utility functions. *
+ * ================== */
+
+#define pushintegerfield(k,v) LCURSES_STMT_BEG {			\
+	lua_pushinteger(L, (lua_Integer) v); lua_setfield(L, -2, k);	\
+} LCURSES_STMT_END
+
+#define pushnumberfield(k,v) LCURSES_STMT_BEG {				\
+	lua_pushnumber(L, (lua_Number) v); lua_setfield(L, -2, k);	\
+} LCURSES_STMT_END
+
+#define pushstringfield(k,v) LCURSES_STMT_BEG {				\
+	if (v) {							\
+		lua_pushstring(L, (const char *) v);			\
+		lua_setfield(L, -2, k);					\
+	}								\
+} LCURSES_STMT_END
+
+#define pushliteralfield(k,v) LCURSES_STMT_BEG {			\
+	if (v) {							\
+		lua_pushliteral(L, v);					\
+		lua_setfield(L, -2, k);					\
+	}								\
+} LCURSES_STMT_END
+
+#define settypemetatable(t) LCURSES_STMT_BEG {				\
+	if (luaL_newmetatable(L, t) == 1)				\
+		pushliteralfield("_type", t);				\
+	lua_setmetatable(L, -2);					\
+} LCURSES_STMT_END
+
+#define setintegerfield(_p, _n) pushintegerfield(LCURSES_STR(_n), _p->_n)
+#define setnumberfield(_p, _n) pushnumberfield(LCURSES_STR(_n), _p->_n)
+#define setstringfield(_p, _n) pushstringfield(LCURSES_STR(_n), _p->_n)
+
+#endif /*LCURSES__HELPERS_C*/
diff --git a/src/lcurses/chstr.c b/src/lcurses/chstr.c
new file mode 100644
index 0000000..2688155
--- /dev/null
+++ b/src/lcurses/chstr.c
@@ -0,0 +1,293 @@
+/*
+ * Curses binding for Lua 5.1, 5.2 & 5.3.
+ *
+ * (c) Gary V. Vaughan <gary@vaughan.pe> 2013-2017
+ * (c) Reuben Thomas <rrt@sc3d.org> 2009-2012
+ * (c) Tiago Dionizio <tiago.dionizio AT gmail.com> 2004-2007
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/***
+Curses attributed string buffers.
+
+An array of characters, plus associated curses attributes and
+colors at each position.
+
+Although marginally useful alone, the constants used to set colors
+and attributes in `chstr` buffers are not defined until **after**
+`curses.initscr ()` has been called.
+
+@classmod curses.chstr
+*/
+
+#ifndef LCURSES_CHSTR_C
+#define LCURSES_CHSTR_C 1
+
+#include "_helpers.c"
+
+
+static const char *CHSTRMETA = "curses:chstr";
+
+
+typedef struct
+{
+	unsigned int len;
+	chtype str[1];
+} chstr;
+#define CHSTR_SIZE(len) (sizeof(chstr) + len * sizeof(chtype))
+
+
+/* create new chstr object and leave it in the lua stack */
+static chstr *
+chstr_new(lua_State *L, int len)
+{
+	chstr *cs;
+
+	if (len < 1)
+		return luaL_error(L, "invalid chstr length"), NULL;
+
+	cs = lua_newuserdata(L, CHSTR_SIZE(len));
+	luaL_getmetatable(L, CHSTRMETA);
+	lua_setmetatable(L, -2);
+	cs->len = len;
+	return cs;
+}
+
+
+/* get chstr from lua (convert if needed) */
+static chstr *
+checkchstr(lua_State *L, int narg)
+{
+	chstr *cs = (chstr*)luaL_checkudata(L, narg, CHSTRMETA);
+	if (cs)
+		return cs;
+
+	luaL_argerror(L, narg, "bad curses chstr");
+
+	/*NOTREACHED*/
+	return NULL;
+}
+
+
+/***
+Change the contents of the chstr.
+@function set_str
+@int o offset to start of change
+@string s characters to insert into *cs* at *o*
+@int[opt=A_NORMAL] attr attributes for changed elements
+@int[opt=1] rep repeat count
+@usage
+  cs = curses.chstr (10)
+  cs:set_str(0, "0123456789", curses.A_BOLD)
+*/
+static int
+Cset_str(lua_State *L)
+{
+	chstr *cs = checkchstr(L, 1);
+	int offset = checkint(L, 2);
+	const char *str = luaL_checkstring(L, 3);
+	int len = lua_strlen(L, 3);
+	int attr = optint(L, 4, A_NORMAL);
+	int rep = optint(L, 5, 1);
+	int i;
+
+	if (offset < 0)
+		return 0;
+
+	while (rep-- > 0 && offset <= (int)cs->len)
+	{
+		if (offset + len - 1 > (int)cs->len)
+			len = cs->len - offset + 1;
+
+		for (i = 0; i < len; ++i)
+			cs->str[offset + i] = str[i] | attr;
+		offset += len;
+	}
+
+	return 0;
+}
+
+
+/***
+Set a character in the buffer.
+*ch* can be a one-character string, or an integer from `string.byte`
+@function set_ch
+@int o offset to start of change
+@param int|string ch character to insert
+@int[opt=A_NORMAL] attr attributes for changed elements
+@int[opt=1] rep repeat count
+@usage
+  -- Write a bold 'A' followed by normal 'a' chars to a new buffer
+  size = 10
+  cs = curses.chstr (size)
+  cs:set_ch(0, 'A', curses.A_BOLD)
+  cs:set_ch(1, 'a', curses.A_NORMAL, size - 1)
+*/
+static int
+Cset_ch(lua_State *L)
+{
+	chstr* cs = checkchstr(L, 1);
+	int offset = checkint(L, 2);
+	chtype ch = checkch(L, 3);
+	int attr = optint(L, 4, A_NORMAL);
+	int rep = optint(L, 5, 1);
+
+	while (rep-- > 0)
+	{
+		if (offset < 0 || offset >= (int) cs->len)
+			return 0;
+
+		cs->str[offset] = ch | attr;
+
+		++offset;
+	}
+	return 0;
+}
+
+
+/***
+Get information from the chstr.
+@function get
+@int o offset from start of *cs*
+@treturn int character at offset *o* in *cs*
+@treturn int bitwise-OR of attributes at offset *o* in *cs*
+@treturn int colorpair at offset *o* in *cs*
+@usage
+  cs = curses.chstr (10)
+  cs:set_ch(0, 'A', curses.A_BOLD, 10)
+  --> 65 2097152 0
+  print (cs:get (9))
+*/
+static int
+Cget(lua_State *L)
+{
+	chstr* cs = checkchstr(L, 1);
+	int offset = checkint(L, 2);
+	chtype ch;
+
+	if (offset < 0 || offset >= (int) cs->len)
+		return 0;
+
+	ch = cs->str[offset];
+
+	lua_pushinteger(L, ch & A_CHARTEXT);
+	lua_pushinteger(L, ch & A_ATTRIBUTES);
+	lua_pushinteger(L, ch & A_COLOR);
+	return 3;
+}
+
+
+/***
+Retrieve chstr length.
+@function len
+@tparam chstr cs buffer to act on
+@treturn int length of *cs*
+@usage
+  cs = curses.chstr (123)
+  --> 123
+  print (cs:len ())
+*/
+static int
+Clen(lua_State *L)
+{
+	chstr *cs = checkchstr(L, 1);
+	return pushintresult(cs->len);
+}
+
+
+/***
+Duplicate chstr.
+@function dup
+@treturn chstr duplicate of *cs*
+@usage
+  dup = cs:dup ()
+*/
+static int
+Cdup(lua_State *L)
+{
+	chstr *cs = checkchstr(L, 1);
+	chstr *ncs = chstr_new(L, cs->len);
+
+	memcpy(ncs->str, cs->str, CHSTR_SIZE(cs->len));
+	return 1;
+}
+
+
+/***
+Initialise a new chstr.
+@function __call
+@int len buffer length
+@treturn chstr a new chstr filled with spaces
+@usage
+  cs = curses.chstr (10)
+*/
+static int
+C__call(lua_State *L)
+{
+	int len = checkint(L, 2);
+	chstr* ncs = chstr_new(L, len);
+	memset(ncs->str, ' ', len * sizeof(chtype));
+	return 1;
+}
+
+
+static const luaL_Reg curses_chstr_fns[] =
+{
+	LCURSES_FUNC( Clen		),
+	LCURSES_FUNC( Cset_ch		),
+	LCURSES_FUNC( Cset_str		),
+	LCURSES_FUNC( Cget		),
+	LCURSES_FUNC( Cdup		),
+	{ NULL, NULL }
+};
+
+
+
+LUALIB_API int
+luaopen_curses_chstr(lua_State *L)
+{
+	int t, mt;
+
+	luaL_register(L, "curses.chstr", curses_chstr_fns);
+	t = lua_gettop(L);
+
+	lua_createtable(L, 0, 1);		/* u = {} */
+	lua_pushcfunction(L, C__call);
+	lua_setfield(L, -2, "__call");		/* u.__call = C__call */
+	lua_setmetatable(L, -2);		/* setmetatable (t, u) */
+
+	luaL_newmetatable(L, CHSTRMETA);
+	mt = lua_gettop(L);
+
+	lua_pushvalue(L, mt);
+	lua_setfield(L, -2, "__index");		/* mt.__index = mt */
+	lua_pushliteral(L, "CursesChstr");
+	lua_setfield(L, -2, "_type");		/* mt._type = "CursesChstr" */
+
+	/* for k,v in pairs(t) do mt[k]=v end */
+	for (lua_pushnil(L); lua_next(L, t) != 0;)
+		lua_setfield(L, mt, lua_tostring(L, -2));
+
+	lua_pop(L, 1);				/* pop mt */
+
+	return 1;
+}
+
+#endif /*!LCURSES_CHSTR_C*/
diff --git a/src/lcurses/compat-5.2.c b/src/lcurses/compat-5.2.c
new file mode 100644
index 0000000..89eb73b
--- /dev/null
+++ b/src/lcurses/compat-5.2.c
@@ -0,0 +1,682 @@
+#include <errno.h>
+#include <string.h>
+
+#include "../lua.h"
+#include "../lauxlib.h"
+#include "compat-5.2.h"
+
+#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM == 501
+
+int lua_absindex (lua_State *L, int i) {
+  if (i < 0 && i > LUA_REGISTRYINDEX)
+    i += lua_gettop(L) + 1;
+  return i;
+}
+
+
+void lua_copy (lua_State *L, int from, int to) {
+  int abs_to = lua_absindex(L, to);
+  luaL_checkstack(L, 1, "not enough stack slots");
+  lua_pushvalue(L, from);
+  lua_replace(L, abs_to);
+}
+
+
+void lua_rawgetp (lua_State *L, int i, const void *p) {
+  int abs_i = lua_absindex(L, i);
+  lua_pushlightuserdata(L, (void*)p);
+  lua_rawget(L, abs_i);
+}
+
+void lua_rawsetp (lua_State *L, int i, const void *p) {
+  int abs_i = lua_absindex(L, i);
+  luaL_checkstack(L, 1, "not enough stack slots");
+  lua_pushlightuserdata(L, (void*)p);
+  lua_insert(L, -2);
+  lua_rawset(L, abs_i);
+}
+
+
+void *luaL_testudata (lua_State *L, int i, const char *tname) {
+  void *p = lua_touserdata(L, i);
+  luaL_checkstack(L, 2, "not enough stack slots");
+  if (p == NULL || !lua_getmetatable(L, i))
+    return NULL;
+  else {
+    int res = 0;
+    luaL_getmetatable(L, tname);
+    res = lua_rawequal(L, -1, -2);
+    lua_pop(L, 2);
+    if (!res)
+      p = NULL;
+  }
+  return p;
+}
+
+
+lua_Number lua_tonumberx (lua_State *L, int i, int *isnum) {
+  lua_Number n = lua_tonumber(L, i);
+  if (isnum != NULL) {
+    *isnum = (n != 0 || lua_isnumber(L, i));
+  }
+  return n;
+}
+
+
+#define PACKAGE_KEY "_COMPAT52_PACKAGE"
+
+static void push_package_table (lua_State *L) {
+  lua_pushliteral(L, PACKAGE_KEY);
+  lua_rawget(L, LUA_REGISTRYINDEX);
+  if (!lua_istable(L, -1)) {
+    lua_pop(L, 1);
+    /* try to get package table from globals */
+    lua_pushliteral(L, "package");
+    lua_rawget(L, LUA_GLOBALSINDEX);
+    if (lua_istable(L, -1)) {
+      lua_pushliteral(L, PACKAGE_KEY);
+      lua_pushvalue(L, -2);
+      lua_rawset(L, LUA_REGISTRYINDEX);
+    }
+  }
+}
+
+void lua_getuservalue (lua_State *L, int i) {
+  luaL_checktype(L, i, LUA_TUSERDATA);
+  luaL_checkstack(L, 2, "not enough stack slots");
+  lua_getfenv(L, i);
+  lua_pushvalue(L, LUA_GLOBALSINDEX);
+  if (lua_rawequal(L, -1, -2)) {
+    lua_pop(L, 1);
+    lua_pushnil(L);
+    lua_replace(L, -2);
+  } else {
+    lua_pop(L, 1);
+    push_package_table(L);
+    if (lua_rawequal(L, -1, -2)) {
+      lua_pop(L, 1);
+      lua_pushnil(L);
+      lua_replace(L, -2);
+    } else
+      lua_pop(L, 1);
+  }
+}
+
+void lua_setuservalue (lua_State *L, int i) {
+  luaL_checktype(L, i, LUA_TUSERDATA);
+  if (lua_isnil(L, -1)) {
+    luaL_checkstack(L, 1, "not enough stack slots");
+    lua_pushvalue(L, LUA_GLOBALSINDEX);
+    lua_replace(L, -2);
+  }
+  lua_setfenv(L, i);
+}
+
+
+/*
+** Adapted from Lua 5.2.0
+*/
+void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) {
+  luaL_checkstack(L, nup+1, "too many upvalues");
+  for (; l->name != NULL; l++) {  /* fill the table with given functions */
+    int i;
+    lua_pushstring(L, l->name);
+    for (i = 0; i < nup; i++)  /* copy upvalues to the top */
+      lua_pushvalue(L, -(nup + 1));
+    lua_pushcclosure(L, l->func, nup);  /* closure with those upvalues */
+    lua_settable(L, -(nup + 3)); /* table must be below the upvalues, the name and the closure */
+  }
+  lua_pop(L, nup);  /* remove upvalues */
+}
+
+
+void luaL_setmetatable (lua_State *L, const char *tname) {
+  luaL_checkstack(L, 1, "not enough stack slots");
+  luaL_getmetatable(L, tname);
+  lua_setmetatable(L, -2);
+}
+
+
+int luaL_getsubtable (lua_State *L, int i, const char *name) {
+  int abs_i = lua_absindex(L, i);
+  luaL_checkstack(L, 3, "not enough stack slots");
+  lua_pushstring(L, name);
+  lua_gettable(L, abs_i);
+  if (lua_istable(L, -1))
+    return 1;
+  lua_pop(L, 1);
+  lua_newtable(L);
+  lua_pushstring(L, name);
+  lua_pushvalue(L, -2);
+  lua_settable(L, abs_i);
+  return 0;
+}
+
+
+#if !defined(COMPAT52_IS_LUAJIT)
+static int countlevels (lua_State *L) {
+  lua_Debug ar;
+  int li = 1, le = 1;
+  /* find an upper bound */
+  while (lua_getstack(L, le, &ar)) { li = le; le *= 2; }
+  /* do a binary search */
+  while (li < le) {
+    int m = (li + le)/2;
+    if (lua_getstack(L, m, &ar)) li = m + 1;
+    else le = m;
+  }
+  return le - 1;
+}
+
+static int findfield (lua_State *L, int objidx, int level) {
+  if (level == 0 || !lua_istable(L, -1))
+    return 0;  /* not found */
+  lua_pushnil(L);  /* start 'next' loop */
+  while (lua_next(L, -2)) {  /* for each pair in table */
+    if (lua_type(L, -2) == LUA_TSTRING) {  /* ignore non-string keys */
+      if (lua_rawequal(L, objidx, -1)) {  /* found object? */
+        lua_pop(L, 1);  /* remove value (but keep name) */
+        return 1;
+      }
+      else if (findfield(L, objidx, level - 1)) {  /* try recursively */
+        lua_remove(L, -2);  /* remove table (but keep name) */
+        lua_pushliteral(L, ".");
+        lua_insert(L, -2);  /* place '.' between the two names */
+        lua_concat(L, 3);
+        return 1;
+      }
+    }
+    lua_pop(L, 1);  /* remove value */
+  }
+  return 0;  /* not found */
+}
+
+static int pushglobalfuncname (lua_State *L, lua_Debug *ar) {
+  int top = lua_gettop(L);
+  lua_getinfo(L, "f", ar);  /* push function */
+  lua_pushvalue(L, LUA_GLOBALSINDEX);
+  if (findfield(L, top + 1, 2)) {
+    lua_copy(L, -1, top + 1);  /* move name to proper place */
+    lua_pop(L, 2);  /* remove pushed values */
+    return 1;
+  }
+  else {
+    lua_settop(L, top);  /* remove function and global table */
+    return 0;
+  }
+}
+
+static void pushfuncname (lua_State *L, lua_Debug *ar) {
+  if (*ar->namewhat != '\0')  /* is there a name? */
+    lua_pushfstring(L, "function " LUA_QS, ar->name);
+  else if (*ar->what == 'm')  /* main? */
+      lua_pushliteral(L, "main chunk");
+  else if (*ar->what == 'C') {
+    if (pushglobalfuncname(L, ar)) {
+      lua_pushfstring(L, "function " LUA_QS, lua_tostring(L, -1));
+      lua_remove(L, -2);  /* remove name */
+    }
+    else
+      lua_pushliteral(L, "?");
+  }
+  else
+    lua_pushfstring(L, "function <%s:%d>", ar->short_src, ar->linedefined);
+}
+
+#define LEVELS1 12  /* size of the first part of the stack */
+#define LEVELS2 10  /* size of the second part of the stack */
+
+void luaL_traceback (lua_State *L, lua_State *L1,
+                                const char *msg, int level) {
+  lua_Debug ar;
+  int top = lua_gettop(L);
+  int numlevels = countlevels(L1);
+  int mark = (numlevels > LEVELS1 + LEVELS2) ? LEVELS1 : 0;
+  if (msg) lua_pushfstring(L, "%s\n", msg);
+  lua_pushliteral(L, "stack traceback:");
+  while (lua_getstack(L1, level++, &ar)) {
+    if (level == mark) {  /* too many levels? */
+      lua_pushliteral(L, "\n\t...");  /* add a '...' */
+      level = numlevels - LEVELS2;  /* and skip to last ones */
+    }
+    else {
+      lua_getinfo(L1, "Slnt", &ar);
+      lua_pushfstring(L, "\n\t%s:", ar.short_src);
+      if (ar.currentline > 0)
+        lua_pushfstring(L, "%d:", ar.currentline);
+      lua_pushliteral(L, " in ");
+      pushfuncname(L, &ar);
+      lua_concat(L, lua_gettop(L) - top);
+    }
+  }
+  lua_concat(L, lua_gettop(L) - top);
+}
+#endif
+
+
+void luaL_checkversion (lua_State *L) {
+  (void)L;
+}
+
+
+#if !defined(COMPAT52_IS_LUAJIT)
+int luaL_fileresult (lua_State *L, int stat, const char *fname) {
+  int en = errno;  /* calls to Lua API may change this value */
+  if (stat) {
+    lua_pushboolean(L, 1);
+    return 1;
+  }
+  else {
+    lua_pushnil(L);
+    if (fname)
+      lua_pushfstring(L, "%s: %s", fname, strerror(en));
+    else
+      lua_pushstring(L, strerror(en));
+    lua_pushnumber(L, (lua_Number)en);
+    return 3;
+  }
+}
+#endif
+
+
+#endif /* Lua 5.0 or Lua 5.1 */
+
+
+#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501
+#include <limits.h>
+
+typedef LUAI_INT32 LUA_INT32;
+
+/********************************************************************/
+/*                    extract of 5.2's luaconf.h                    */
+/*  detects proper defines for faster unsigned<->number conversion  */
+/*           see copyright notice at the end of this file           */
+/********************************************************************/
+
+#if !defined(LUA_ANSI) && defined(_WIN32) && !defined(_WIN32_WCE)
+#define LUA_WIN		/* enable goodies for regular Windows platforms */
+#endif
+
+
+#if defined(LUA_NUMBER_DOUBLE) && !defined(LUA_ANSI)	/* { */
+
+/* Microsoft compiler on a Pentium (32 bit) ? */
+#if defined(LUA_WIN) && defined(_MSC_VER) && defined(_M_IX86)	/* { */
+
+#define LUA_MSASMTRICK
+#define LUA_IEEEENDIAN		0
+#define LUA_NANTRICK
+
+/* pentium 32 bits? */
+#elif defined(__i386__) || defined(__i386) || defined(__X86__) /* }{ */
+
+#define LUA_IEEE754TRICK
+#define LUA_IEEELL
+#define LUA_IEEEENDIAN		0
+#define LUA_NANTRICK
+
+/* pentium 64 bits? */
+#elif defined(__x86_64)						/* }{ */
+
+#define LUA_IEEE754TRICK
+#define LUA_IEEEENDIAN		0
+
+#elif defined(__POWERPC__) || defined(__ppc__)			/* }{ */
+
+#define LUA_IEEE754TRICK
+#define LUA_IEEEENDIAN		1
+
+#else								/* }{ */
+
+/* assume IEEE754 and a 32-bit integer type */
+#define LUA_IEEE754TRICK
+
+#endif								/* } */
+
+#endif							/* } */
+
+
+/********************************************************************/
+/*                    extract of 5.2's llimits.h                    */
+/*       gives us lua_number2unsigned and lua_unsigned2number       */
+/*           see copyright notice at the end of this file           */
+/********************************************************************/
+
+#if defined(MS_ASMTRICK) || defined(LUA_MSASMTRICK)	/* { */
+/* trick with Microsoft assembler for X86 */
+
+#define lua_number2unsigned(i,n)  \
+  {__int64 l; __asm {__asm fld n   __asm fistp l} i = (unsigned int)l;}
+
+
+#elif defined(LUA_IEEE754TRICK)		/* }{ */
+/* the next trick should work on any machine using IEEE754 with
+   a 32-bit int type */
+
+union compat52_luai_Cast { double l_d; LUA_INT32 l_p[2]; };
+
+#if !defined(LUA_IEEEENDIAN)	/* { */
+#define LUAI_EXTRAIEEE	\
+  static const union compat52_luai_Cast ieeeendian = {-(33.0 + 6755399441055744.0)};
+#define LUA_IEEEENDIANLOC	(ieeeendian.l_p[1] == 33)
+#else
+#define LUA_IEEEENDIANLOC	LUA_IEEEENDIAN
+#define LUAI_EXTRAIEEE		/* empty */
+#endif				/* } */
+
+#define lua_number2int32(i,n,t) \
+  { LUAI_EXTRAIEEE \
+    volatile union compat52_luai_Cast u; u.l_d = (n) + 6755399441055744.0; \
+    (i) = (t)u.l_p[LUA_IEEEENDIANLOC]; }
+
+#define lua_number2unsigned(i,n)	lua_number2int32(i, n, lua_Unsigned)
+
+#endif				/* } */
+
+
+/* the following definitions always work, but may be slow */
+
+#if !defined(lua_number2unsigned)	/* { */
+/* the following definition assures proper modulo behavior */
+#if defined(LUA_NUMBER_DOUBLE) || defined(LUA_NUMBER_FLOAT)
+#include <math.h>
+#define SUPUNSIGNED	((lua_Number)(~(lua_Unsigned)0) + 1)
+#define lua_number2unsigned(i,n)  \
+	((i)=(lua_Unsigned)((n) - floor((n)/SUPUNSIGNED)*SUPUNSIGNED))
+#else
+#define lua_number2unsigned(i,n)	((i)=(lua_Unsigned)(n))
+#endif
+#endif				/* } */
+
+
+#if !defined(lua_unsigned2number)
+/* on several machines, coercion from unsigned to double is slow,
+   so it may be worth to avoid */
+#define lua_unsigned2number(u)  \
+    (((u) <= (lua_Unsigned)INT_MAX) ? (lua_Number)(int)(u) : (lua_Number)(u))
+#endif
+
+/********************************************************************/
+
+
+static void compat52_call_lua (lua_State *L, char const code[], size_t len,
+                               int nargs, int nret) {
+  lua_rawgetp(L, LUA_REGISTRYINDEX, (void*)code);
+  if (lua_type(L, -1) != LUA_TFUNCTION) {
+    lua_pop(L, 1);
+    if (luaL_loadbuffer(L, code, len, "=none"))
+      lua_error(L);
+    lua_pushvalue(L, -1);
+    lua_rawsetp(L, LUA_REGISTRYINDEX, (void*)code);
+  }
+  lua_insert(L, -nargs-1);
+  lua_call(L, nargs, nret);
+}
+
+static const char compat52_arith_code[] = {
+  'l', 'o', 'c', 'a', 'l', ' ', 'o', 'p', ',', 'a', ',', 'b',
+  '=', '.', '.', '.', '\n',
+  'i', 'f', ' ', 'o', 'p', '=', '=', '0', ' ',
+  't', 'h', 'e', 'n', '\n',
+  'r', 'e', 't', 'u', 'r', 'n', ' ', 'a', '+', 'b', '\n',
+  'e', 'l', 's', 'e', 'i', 'f', ' ', 'o', 'p', '=', '=', '1', ' ',
+  't', 'h', 'e', 'n', '\n',
+  'r', 'e', 't', 'u', 'r', 'n', ' ', 'a', '-', 'b', '\n',
+  'e', 'l', 's', 'e', 'i', 'f', ' ', 'o', 'p', '=', '=', '2', ' ',
+  't', 'h', 'e', 'n', '\n',
+  'r', 'e', 't', 'u', 'r', 'n', ' ', 'a', '*', 'b', '\n',
+  'e', 'l', 's', 'e', 'i', 'f', ' ', 'o', 'p', '=', '=', '3', ' ',
+  't', 'h', 'e', 'n', '\n',
+  'r', 'e', 't', 'u', 'r', 'n', ' ', 'a', '/', 'b', '\n',
+  'e', 'l', 's', 'e', 'i', 'f', ' ', 'o', 'p', '=', '=', '4', ' ',
+  't', 'h', 'e', 'n', '\n',
+  'r', 'e', 't', 'u', 'r', 'n', ' ', 'a', '%', 'b', '\n',
+  'e', 'l', 's', 'e', 'i', 'f', ' ', 'o', 'p', '=', '=', '5', ' ',
+  't', 'h', 'e', 'n', '\n',
+  'r', 'e', 't', 'u', 'r', 'n', ' ', 'a', '^', 'b', '\n',
+  'e', 'l', 's', 'e', 'i', 'f', ' ', 'o', 'p', '=', '=', '6', ' ',
+  't', 'h', 'e', 'n', '\n',
+  'r', 'e', 't', 'u', 'r', 'n', ' ', '-', 'a', '\n',
+  'e', 'n', 'd', '\n', '\0'
+};
+
+void lua_arith (lua_State *L, int op) {
+  if (op < LUA_OPADD && op > LUA_OPUNM)
+    luaL_error(L, "invalid 'op' argument for lua_arith");
+  luaL_checkstack(L, 5, "not enough stack slots");
+  if (op == LUA_OPUNM)
+    lua_pushvalue(L, -1);
+  lua_pushnumber(L, op);
+  lua_insert(L, -3);
+  compat52_call_lua(L, compat52_arith_code,
+                    sizeof(compat52_arith_code)-1, 3, 1);
+}
+
+
+static const char compat52_compare_code[] = {
+  'l', 'o', 'c', 'a', 'l', ' ', 'a', ',', 'b', '=', '.', '.', '.', '\n',
+  'r', 'e', 't', 'u', 'r', 'n', ' ', 'a', '<', '=', 'b', '\n', '\0'
+};
+
+int lua_compare (lua_State *L, int idx1, int idx2, int op) {
+  int result = 0;
+  switch (op) {
+    case LUA_OPEQ:
+      return lua_equal(L, idx1, idx2);
+    case LUA_OPLT:
+      return lua_lessthan(L, idx1, idx2);
+    case LUA_OPLE:
+      luaL_checkstack(L, 5, "not enough stack slots");
+      idx1 = lua_absindex(L, idx1);
+      idx2 = lua_absindex(L, idx2);
+      lua_pushvalue(L, idx1);
+      lua_pushvalue(L, idx2);
+      compat52_call_lua(L, (void*)compat52_compare_code,
+                        sizeof(compat52_compare_code)-1, 2, 1);
+      result = lua_toboolean(L, -1);
+      lua_pop(L, 1);
+      return result;
+    default:
+      luaL_error(L, "invalid 'op' argument for lua_compare");
+  }
+  return 0;
+}
+
+
+void lua_pushunsigned (lua_State *L, lua_Unsigned n) {
+  lua_pushnumber(L, lua_unsigned2number(n));
+}
+
+
+lua_Unsigned luaL_checkunsigned (lua_State *L, int i) {
+  lua_Unsigned result;
+  lua_Number n = lua_tonumber(L, i);
+  if (n == 0 && !lua_isnumber(L, i))
+    luaL_checktype(L, i, LUA_TNUMBER);
+  lua_number2unsigned(result, n);
+  return result;
+}
+
+
+lua_Unsigned lua_tounsignedx (lua_State *L, int i, int *isnum) {
+  lua_Unsigned result;
+  lua_Number n = lua_tonumberx(L, i, isnum);
+  lua_number2unsigned(result, n);
+  return result;
+}
+
+
+lua_Unsigned luaL_optunsigned (lua_State *L, int i, lua_Unsigned def) {
+  return luaL_opt(L, luaL_checkunsigned, i, def);
+}
+
+
+lua_Integer lua_tointegerx (lua_State *L, int i, int *isnum) {
+  lua_Integer n = lua_tointeger(L, i);
+  if (isnum != NULL) {
+    *isnum = (n != 0 || lua_isnumber(L, i));
+  }
+  return n;
+}
+
+
+void lua_len (lua_State *L, int i) {
+  switch (lua_type(L, i)) {
+    case LUA_TSTRING: /* fall through */
+    case LUA_TTABLE:
+      if (!luaL_callmeta(L, i, "__len"))
+        lua_pushnumber(L, (int)lua_objlen(L, i));
+      break;
+    case LUA_TUSERDATA:
+      if (luaL_callmeta(L, i, "__len"))
+        break;
+      /* maybe fall through */
+    default:
+      luaL_error(L, "attempt to get length of a %s value",
+                 lua_typename(L, lua_type(L, i)));
+  }
+}
+
+
+int luaL_len (lua_State *L, int i) {
+  int res = 0, isnum = 0;
+  luaL_checkstack(L, 1, "not enough stack slots");
+  lua_len(L, i);
+  res = (int)lua_tointegerx(L, -1, &isnum);
+  lua_pop(L, 1);
+  if (!isnum)
+    luaL_error(L, "object length is not a number");
+  return res;
+}
+
+
+const char *luaL_tolstring (lua_State *L, int idx, size_t *len) {
+  if (!luaL_callmeta(L, idx, "__tostring")) {
+    int t = lua_type(L, idx);
+    switch (t) {
+      case LUA_TNIL:
+        lua_pushliteral(L, "nil");
+        break;
+      case LUA_TSTRING:
+      case LUA_TNUMBER:
+        lua_pushvalue(L, idx);
+        break;
+      case LUA_TBOOLEAN:
+        if (lua_toboolean(L, idx))
+          lua_pushliteral(L, "true");
+        else
+          lua_pushliteral(L, "false");
+        break;
+      default:
+        lua_pushfstring(L, "%s: %p", lua_typename(L, t),
+                                     lua_topointer(L, idx));
+        break;
+    }
+  }
+  return lua_tolstring(L, -1, len);
+}
+
+
+void luaL_requiref (lua_State *L, char const* modname,
+                    lua_CFunction openf, int glb) {
+  luaL_checkstack(L, 3, "not enough stack slots");
+  lua_pushcfunction(L, openf);
+  lua_pushstring(L, modname);
+  lua_call(L, 1, 1);
+  lua_getglobal(L, "package");
+  lua_getfield(L, -1, "loaded");
+  lua_replace(L, -2);
+  lua_pushvalue(L, -2);
+  lua_setfield(L, -2, modname);
+  lua_pop(L, 1);
+  if (glb) {
+    lua_pushvalue(L, -1);
+    lua_setglobal(L, modname);
+  }
+}
+
+
+void luaL_buffinit (lua_State *L, luaL_Buffer_52 *B) {
+  /* make it crash if used via pointer to a 5.1-style luaL_Buffer */
+  B->b.p = NULL;
+  B->b.L = NULL;
+  B->b.lvl = 0;
+  /* reuse the buffer from the 5.1-style luaL_Buffer though! */
+  B->ptr = B->b.buffer;
+  B->capacity = LUAL_BUFFERSIZE;
+  B->nelems = 0;
+  B->L2 = L;
+}
+
+
+char *luaL_prepbuffsize (luaL_Buffer_52 *B, size_t s) {
+  if (B->capacity - B->nelems < s) { /* needs to grow */
+    char* newptr = NULL;
+    size_t newcap = B->capacity * 2;
+    if (newcap - B->nelems < s)
+      newcap = B->nelems + s;
+    if (newcap < B->capacity) /* overflow */
+      luaL_error(B->L2, "buffer too large");
+    newptr = lua_newuserdata(B->L2, newcap);
+    memcpy(newptr, B->ptr, B->nelems);
+    if (B->ptr != B->b.buffer)
+      lua_replace(B->L2, -2); /* remove old buffer */
+    B->ptr = newptr;
+    B->capacity = newcap;
+  }
+  return B->ptr+B->nelems;
+}
+
+
+void luaL_addlstring (luaL_Buffer_52 *B, const char *s, size_t l) {
+  memcpy(luaL_prepbuffsize(B, l), s, l);
+  luaL_addsize(B, l);
+}
+
+
+void luaL_addvalue (luaL_Buffer_52 *B) {
+  size_t len = 0;
+  const char *s = lua_tolstring(B->L2, -1, &len);
+  if (!s)
+    luaL_error(B->L2, "cannot convert value to string");
+  if (B->ptr != B->b.buffer)
+    lua_insert(B->L2, -2); /* userdata buffer must be at stack top */
+  luaL_addlstring(B, s, len);
+  lua_remove(B->L2, B->ptr != B->b.buffer ? -2 : -1);
+}
+
+
+void luaL_pushresult (luaL_Buffer_52 *B) {
+  lua_pushlstring(B->L2, B->ptr, B->nelems);
+  if (B->ptr != B->b.buffer)
+    lua_replace(B->L2, -2); /* remove userdata buffer */
+}
+
+
+#endif /* LUA_VERSION_NUM == 501 */
+
+
+/*********************************************************************
+* This file contains parts of Lua 5.2's source code:
+*
+* Copyright (C) 1994-2013 Lua.org, PUC-Rio.
+*
+* Permission is hereby granted, free of charge, to any person obtaining
+* a copy of this software and associated documentation files (the
+* "Software"), to deal in the Software without restriction, including
+* without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+*
+* The above copyright notice and this permission notice shall be
+* included in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*********************************************************************/
diff --git a/src/lcurses/compat-5.2.h b/src/lcurses/compat-5.2.h
new file mode 100644
index 0000000..119e0d8
--- /dev/null
+++ b/src/lcurses/compat-5.2.h
@@ -0,0 +1,168 @@
+#include <stddef.h>
+#include <string.h>
+#include <stdio.h>
+#include "../lua.h"
+#include "../lauxlib.h"
+#include "../lualib.h"
+
+#if !defined(LUA_VERSION_NUM)
+/* Lua 5.0 */
+
+#define LUA_QL(x) "'" x "'"
+#define LUA_QS LUA_QL("%s")
+
+#define luaL_Reg luaL_reg
+
+#define luaL_opt(L, f, n, d) \
+  (lua_isnoneornil(L, n) ? (d) : f(L, n))
+
+#define luaL_addchar(B,c) \
+  ((void)((B)->p < ((B)->buffer+LUAL_BUFFERSIZE) || luaL_prepbuffer(B)), \
+   (*(B)->p++ = (char)(c)))
+
+#endif /* Lua 5.0 */
+
+
+#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501
+/* Lua 5.1 */
+
+/* PUC-Rio Lua uses lconfig_h as include guard for luaconf.h,
+ * LuaJIT uses luaconf_h. If you use PUC-Rio's include files
+ * but LuaJIT's library, you will need to define the macro
+ * COMPAT52_IS_LUAJIT yourself! */
+#if !defined(COMPAT52_IS_LUAJIT) && defined(luaconf_h)
+#define COMPAT52_IS_LUAJIT
+#endif
+
+/* LuaJIT doesn't define these unofficial macros ... */
+#if !defined(LUAI_INT32)
+#include <limits.h>
+#if INT_MAX-20 < 32760
+#define LUAI_INT32  long
+#define LUAI_UINT32 unsigned long
+#elif INT_MAX > 2147483640L
+#define LUAI_INT32  int
+#define LUAI_UINT32 unsigned int
+#else
+#error "could not detect suitable lua_Unsigned datatype"
+#endif
+#endif
+
+#define LUA_OPADD 0
+#define LUA_OPSUB 1
+#define LUA_OPMUL 2
+#define LUA_OPDIV 3
+#define LUA_OPMOD 4
+#define LUA_OPPOW 5
+#define LUA_OPUNM 6
+#define LUA_OPEQ 0
+#define LUA_OPLT 1
+#define LUA_OPLE 2
+
+typedef LUAI_UINT32 lua_Unsigned;
+
+typedef struct luaL_Buffer_52 {
+  luaL_Buffer b; /* make incorrect code crash! */
+  char *ptr;
+  size_t nelems;
+  size_t capacity;
+  lua_State *L2;
+} luaL_Buffer_52;
+#define luaL_Buffer luaL_Buffer_52
+
+typedef struct luaL_Stream {
+  FILE *f;
+  /* The following field is for LuaJIT which adds a uint32_t field
+   * to file handles. */
+  lua_Unsigned type;
+  lua_CFunction closef;
+} luaL_Stream;
+
+
+#define lua_tounsigned(L, i) lua_tounsignedx(L, i, NULL)
+
+#define lua_rawlen(L, i) lua_objlen(L, i)
+
+void lua_arith (lua_State *L, int op);
+int lua_compare (lua_State *L, int idx1, int idx2, int op);
+void lua_pushunsigned (lua_State *L, lua_Unsigned n);
+lua_Unsigned luaL_checkunsigned (lua_State *L, int i);
+lua_Unsigned lua_tounsignedx (lua_State *L, int i, int *isnum);
+lua_Unsigned luaL_optunsigned (lua_State *L, int i, lua_Unsigned def);
+lua_Integer lua_tointegerx (lua_State *L, int i, int *isnum);
+void lua_len (lua_State *L, int i);
+int luaL_len (lua_State *L, int i);
+const char *luaL_tolstring (lua_State *L, int idx, size_t *len);
+void luaL_requiref (lua_State *L, char const* modname, lua_CFunction openf, int glb);
+
+#define luaL_buffinit luaL_buffinit_52
+void luaL_buffinit (lua_State *L, luaL_Buffer_52 *B);
+
+#define luaL_prepbuffsize luaL_prepbuffsize_52
+char *luaL_prepbuffsize (luaL_Buffer_52 *B, size_t s);
+
+#define luaL_addlstring luaL_addlstring_52
+void luaL_addlstring (luaL_Buffer_52 *B, const char *s, size_t l);
+
+#define luaL_addvalue luaL_addvalue_52
+void luaL_addvalue (luaL_Buffer_52 *B);
+
+#define luaL_pushresult luaL_pushresult_52
+void luaL_pushresult (luaL_Buffer_52 *B);
+
+#undef luaL_buffinitsize
+#define luaL_buffinitsize(L, B, s) \
+  (luaL_buffinit(L, B), luaL_prepbuffsize(B, s))
+
+#undef luaL_prepbuffer
+#define luaL_prepbuffer(B) \
+  luaL_prepbuffsize(B, LUAL_BUFFERSIZE)
+
+#undef luaL_addchar
+#define luaL_addchar(B, c) \
+  ((void)((B)->nelems < (B)->capacity || luaL_prepbuffsize(B, 1)), \
+   ((B)->ptr[(B)->nelems++] = (c)))
+
+#undef luaL_addsize
+#define luaL_addsize(B, s) \
+  ((B)->nelems += (s))
+
+#undef luaL_addstring
+#define luaL_addstring(B, s) \
+  luaL_addlstring(B, s, strlen(s))
+
+#undef luaL_pushresultsize
+#define luaL_pushresultsize(B, s) \
+  (luaL_addsize(B, s), luaL_pushresult(B))
+
+#endif /* Lua 5.1 */
+
+
+#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM == 501
+/* Lua 5.0 *or* 5.1 */
+
+#define LUA_OK 0
+
+#define lua_pushglobaltable(L) \
+  lua_pushvalue(L, LUA_GLOBALSINDEX)
+
+#define luaL_newlib(L, l) \
+  (lua_newtable((L)),luaL_setfuncs((L), (l), 0))
+
+void luaL_checkversion (lua_State *L);
+
+#endif /* Lua 5.0 *or* 5.1 */
+
+int lua_absindex (lua_State *L, int i);
+void lua_copy (lua_State *L, int from, int to);
+void lua_rawgetp (lua_State *L, int i, const void *p);
+void lua_rawsetp (lua_State *L, int i, const void *p);
+void *luaL_testudata (lua_State *L, int i, const char *tname);
+lua_Number lua_tonumberx (lua_State *L, int i, int *isnum);
+void lua_getuservalue (lua_State *L, int i);
+void lua_setuservalue (lua_State *L, int i);
+void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup);
+void luaL_setmetatable (lua_State *L, const char *tname);
+int luaL_getsubtable (lua_State *L, int i, const char *name);
+void luaL_traceback (lua_State *L, lua_State *L1, const char *msg, int level);
+int luaL_fileresult (lua_State *L, int stat, const char *fname);
diff --git a/src/lcurses/curses.c b/src/lcurses/curses.c
new file mode 100644
index 0000000..c5f7c3e
--- /dev/null
+++ b/src/lcurses/curses.c
@@ -0,0 +1,1555 @@
+/*
+ * Curses binding for Lua 5.1, 5.2 & 5.3.
+ *
+ * (c) Gary V. Vaughan <gary@vaughan.pe> 2013-2017
+ * (c) Reuben Thomas <rrt@sc3d.org> 2009-2012
+ * (c) Tiago Dionizio <tiago.dionizio AT gmail.com> 2004-2007
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/***
+ Full-screen Text Terminal Manipulation.
+
+ In the underlying curses C library, the following functions:
+
+     getstr()   (and wgetstr(), mvgetstr(), and mvwgetstr())
+     inchstr()  (and winchstr(), mvinchstr(), and mvwinchstr())
+     instr()    (and winstr(), mvinstr(), and mvwinstr())
+
+ are subject to buffer overflow attack. This is because you pass in the
+ buffer to be filled in, which has to be of finite length. But in this
+ Lua module, a buffer is assigned automatically and the function returns
+ the string, so there is no security issue. You may still use the alternate
+ functions:
+
+     s = stdscr:getnstr()
+     s = stdscr:inchnstr()
+     s = stdscr:innstr()
+
+ which take an extra "size of buffer" argument, in order to impose a maximum
+ length on the string the user may type in.
+
+ Some of the C functions beginning with "no" do not exist in Lua. You should
+ use `curses.nl(false)` and `curses.nl(true)` instead of `nonl()` and `nl()`,
+ and likewise `curses.echo(false)` and `curses.echo(true)` instead of
+ `noecho()` and `echo()` .
+
+ In this Lua module the `stdscr:getch()` function always returns an integer.
+ In C, a single character is an integer, but in Lua (and Perl) a single
+ character is a short string. The Perl Curses function `getch()` returns a
+ char if it was a char, and a number if it was a constant; to get this
+ behaviour in Lua you have to convert explicitly, e.g.:
+
+     if c < 256 then c = string.char(c) end
+
+ Some Lua functions take a different set of parameters than their C
+ counterparts; for example, you should use `str = stdscr.getstr()` and
+ `y, x = stdscr.getyx()` instead of `getstr(str)` and `getyx(y, x)`, and
+ likewise for `getbegyx` and `getmaxyx` and `getparyx` and `pair_content`.
+ The Perl Curses module now uses the C-compatible parameters, so be aware of
+ this difference when translating code from Perl into Lua, as well as from C
+ into Lua.
+
+ Many curses functions have variants starting with the prefixes `w-`, `mv-`,
+ and/or `wmv-`. These variants differ only in the explicit addition of a
+ window, or by the addition of two coordinates that are used to move the
+ cursor first. For example, in C `addch()` has three other variants:
+ `waddch()`, `mvaddch()` and `mvwaddch()`.  The Lua equivalents,
+ respectively being `stdscr:addch()`, `somewindow:addch()`,
+ `stdscr:mvaddch()` and `somewindow:mvaddch()`, with the window argument
+ passed implicitly with Lua's `:` syntax sugar.
+
+@module curses
+*/
+
+
+#include "_helpers.c"
+#include "strlcpy.c"
+
+#include "chstr.c"
+#include "window.c"
+
+static const char *STDSCR_REGISTRY	= "curses:stdscr";
+static const char *RIPOFF_TABLE		= "curses:ripoffline";
+
+
+/***
+Create a new line drawing buffer instance.
+@function new_chstr
+@int len number of element to allocate
+@treturn chstr a new char buffer object
+@see curses.chstr
+*/
+static int
+Pnew_chstr(lua_State *L)
+{
+	int len = checkint(L, 1);
+	chstr* ncs = chstr_new(L, len);  /* defined in curses/chstr.c */
+	memset(ncs->str, ' ', len*sizeof(chtype));
+	return 1;
+}
+
+
+
+#define CCR(n, v)				\
+	lua_pushstring(L, n);			\
+	lua_pushinteger(L, v);			\
+	lua_settable(L, lua_upvalueindex(1))
+
+#define CC(s)	   CCR(#s, s)
+#define CF(i)	   CCR(LCURSES_STR(LCURSES_SPLICE(KEY_F, i)), KEY_F(i))
+
+/*
+** these values may be fixed only after initialization, so this is
+** called from Pinitscr, after the curses driver is initialized
+**
+** curses table is kept at upvalue position 1, in case the global
+** name is changed by the user or even in the registration phase by
+** the developer
+**
+** some of these values are not constant so need to register
+** them directly instead of using a table
+*/
+
+static void
+register_curses_constants(lua_State *L)
+{
+	/* colors */
+	CC(COLOR_BLACK);	CC(COLOR_RED);		CC(COLOR_GREEN);
+	CC(COLOR_YELLOW);	CC(COLOR_BLUE);		CC(COLOR_MAGENTA);
+	CC(COLOR_CYAN);		CC(COLOR_WHITE);
+
+	/* alternate character set */
+	CC(ACS_BLOCK);		CC(ACS_BOARD);
+
+	CC(ACS_BTEE);		CC(ACS_TTEE);
+	CC(ACS_LTEE);		CC(ACS_RTEE);
+	CC(ACS_LLCORNER);	CC(ACS_LRCORNER);
+	CC(ACS_URCORNER);	CC(ACS_ULCORNER);
+
+	CC(ACS_LARROW);		CC(ACS_RARROW);
+	CC(ACS_UARROW);		CC(ACS_DARROW);
+
+	CC(ACS_HLINE);		CC(ACS_VLINE);
+
+	CC(ACS_BULLET);		CC(ACS_CKBOARD);	CC(ACS_LANTERN);
+	CC(ACS_DEGREE);		CC(ACS_DIAMOND);
+
+	CC(ACS_PLMINUS);	CC(ACS_PLUS);
+	CC(ACS_S1);		CC(ACS_S9);
+
+	/* attributes */
+	CC(A_NORMAL);		CC(A_STANDOUT);		CC(A_UNDERLINE);
+	CC(A_REVERSE);		CC(A_BLINK);		CC(A_DIM);
+	CC(A_BOLD);		CC(A_PROTECT);		CC(A_INVIS);
+	CC(A_ALTCHARSET);	CC(A_CHARTEXT);
+	CC(A_ATTRIBUTES);
+#ifdef A_COLOR
+	CC(A_COLOR);
+#endif
+
+	/* key functions */
+	CC(KEY_BREAK);		CC(KEY_DOWN);		CC(KEY_UP);
+	CC(KEY_LEFT);		CC(KEY_RIGHT);		CC(KEY_HOME);
+	CC(KEY_BACKSPACE);
+
+	CC(KEY_DL);		CC(KEY_IL);		CC(KEY_DC);
+	CC(KEY_IC);		CC(KEY_EIC);		CC(KEY_CLEAR);
+	CC(KEY_EOS);		CC(KEY_EOL);		CC(KEY_SF);
+	CC(KEY_SR);		CC(KEY_NPAGE);		CC(KEY_PPAGE);
+	CC(KEY_STAB);		CC(KEY_CTAB);		CC(KEY_CATAB);
+	CC(KEY_ENTER);		CC(KEY_SRESET);		CC(KEY_RESET);
+	CC(KEY_PRINT);		CC(KEY_LL);		CC(KEY_A1);
+	CC(KEY_A3);		CC(KEY_B2);		CC(KEY_C1);
+	CC(KEY_C3);		CC(KEY_BTAB);		CC(KEY_BEG);
+	CC(KEY_CANCEL);		CC(KEY_CLOSE);		CC(KEY_COMMAND);
+	CC(KEY_COPY);		CC(KEY_CREATE);		CC(KEY_END);
+	CC(KEY_EXIT);		CC(KEY_FIND);		CC(KEY_HELP);
+	CC(KEY_MARK);		CC(KEY_MESSAGE); /* ncurses extension: CC(KEY_MOUSE); */
+	CC(KEY_MOVE);		CC(KEY_NEXT);		CC(KEY_OPEN);
+	CC(KEY_OPTIONS);	CC(KEY_PREVIOUS);	CC(KEY_REDO);
+	CC(KEY_REFERENCE);	CC(KEY_REFRESH);	CC(KEY_REPLACE);
+	CC(KEY_RESIZE);		CC(KEY_RESTART);	CC(KEY_RESUME);
+	CC(KEY_SAVE);		CC(KEY_SBEG);		CC(KEY_SCANCEL);
+	CC(KEY_SCOMMAND);	CC(KEY_SCOPY);		CC(KEY_SCREATE);
+	CC(KEY_SDC);		CC(KEY_SDL);		CC(KEY_SELECT);
+	CC(KEY_SEND);		CC(KEY_SEOL);		CC(KEY_SEXIT);
+	CC(KEY_SFIND);		CC(KEY_SHELP);		CC(KEY_SHOME);
+	CC(KEY_SIC);		CC(KEY_SLEFT);		CC(KEY_SMESSAGE);
+	CC(KEY_SMOVE);		CC(KEY_SNEXT);		CC(KEY_SOPTIONS);
+	CC(KEY_SPREVIOUS);	CC(KEY_SPRINT);		CC(KEY_SREDO);
+	CC(KEY_SREPLACE);	CC(KEY_SRIGHT);		CC(KEY_SRSUME);
+	CC(KEY_SSAVE);		CC(KEY_SSUSPEND);	CC(KEY_SUNDO);
+	CC(KEY_SUSPEND);	CC(KEY_UNDO);
+
+	/* KEY_Fx  0 <= x <= 63 */
+	CC(KEY_F0);
+	CF(1);  CF(2);  CF(3);  CF(4);  CF(5);  CF(6);  CF(7);  CF(8);
+	CF(9);  CF(10); CF(11); CF(12); CF(13); CF(14); CF(15); CF(16);
+	CF(17); CF(18); CF(19); CF(20); CF(21); CF(22); CF(23); CF(24);
+	CF(25); CF(26); CF(27); CF(28); CF(29); CF(30); CF(31); CF(32);
+	CF(33); CF(34); CF(35); CF(36); CF(37); CF(38); CF(39); CF(40);
+	CF(41); CF(42); CF(43); CF(44); CF(45); CF(46); CF(47); CF(48);
+	CF(49); CF(50); CF(51); CF(52); CF(53); CF(54); CF(55); CF(56);
+	CF(57); CF(58); CF(59); CF(60); CF(61); CF(62); CF(63);
+}
+
+/*
+** make sure screen is restored (and cleared) at exit
+** (for the situations where program is aborted without a
+** proper cleanup)
+*/
+
+static void
+cleanup(void)
+{
+	if (!isendwin())
+	{
+		wclear(stdscr);
+		wrefresh(stdscr);
+		endwin();
+	}
+}
+
+
+/***
+Initialise screen.
+@function initscr
+@treturn window main screen
+@see initscr(3x)
+*/
+static int
+Pinitscr(lua_State *L)
+{
+	WINDOW *w;
+
+	/* initialize curses */
+	w = initscr();
+
+	/* no longer used, so clean it up */
+	lua_pushstring(L, RIPOFF_TABLE);
+	lua_pushnil(L);
+	lua_settable(L, LUA_REGISTRYINDEX);
+
+	/* failed to initialize */
+	if (w == NULL)
+		return 0;
+
+	/* return stdscr - main window */
+	lc_newwin(L, w);
+
+	/* save main window on registry */
+	lua_pushstring(L, STDSCR_REGISTRY);
+	lua_pushvalue(L, -2);
+	lua_rawset(L, LUA_REGISTRYINDEX);
+
+	/* setup curses constants - curses.xxx numbers */
+	register_curses_constants(L);
+
+	/* install cleanup handler to help in debugging and screen trashing */
+	atexit(cleanup);
+
+	return 1;
+}
+
+
+/***
+Clean up terminal prior to exiting or escaping curses.
+@function endwin
+@treturn bool `true`, if successful
+@see endwin(3x)
+*/
+static int
+Pendwin(lua_State *L)
+{
+	return pushokresult(endwin());
+}
+
+
+/***
+Has @{endwin} been called more recently than @{curses.window:refresh}?
+@function isendwin
+@treturn bool whether @{endwin} has been called
+@see isendwin(3x)
+*/
+static int
+Pisendwin(lua_State *L)
+{
+	return pushboolresult(isendwin());
+}
+
+
+/***
+Retern the main screen window.
+@function stdscr
+@treturn window main screen
+@see initscr
+@see stdscr(3x)
+*/
+static int
+Pstdscr(lua_State *L)
+{
+	lua_pushstring(L, STDSCR_REGISTRY);
+	lua_rawget(L, LUA_REGISTRYINDEX);
+	return 1;
+}
+
+
+/***
+Number of columns in the main screen window.
+@function cols
+@treturn int number of columns in the main screen
+@see lines
+@see stdscr
+@see COLS(3x)
+*/
+static int
+Pcols(lua_State *L)
+{
+	return pushintresult(COLS);
+}
+
+
+/***
+Number of lines in the main screen window.
+@function lines
+@treturn int number of lines in the main screen
+@see cols
+@see stdscr
+@see LINES(3x)
+*/
+static int
+Plines(lua_State *L)
+{
+	return pushintresult(LINES);
+}
+
+
+/***
+Initialise color output facility.
+@function start_color
+@treturn bool `true`, if successful
+@see can_change_color(3x)
+@see has_colors
+*/
+static int
+Pstart_color(lua_State *L)
+{
+	return pushokresult(start_color());
+}
+
+
+/***
+Does the terminal have color capability?
+@function has_colors
+@treturn bool `true`, if the terminal supports colors
+@see can_change_color(3x)
+@see start_color
+@usage
+if curses.has_colors () then
+  curses.start_color ()
+end
+*/
+static int
+Phas_colors(lua_State *L)
+{
+	return pushboolresult(has_colors());
+}
+
+
+/***
+Reserve `-1` to represent terminal default colors.
+@function use_default_colors
+@treturn bool `true`, if successful
+@see use_default_colors(3x)
+@fixme ncurses only?
+*/
+static int
+Puse_default_colors(lua_State *L)
+{
+	return pushokresult(use_default_colors());
+}
+
+
+/***
+Associate a color pair id with a specific foreground and background color.
+@function init_pair
+@int pair color pair id to act on
+@int f foreground color to assign
+@int b background color to assign
+@treturn bool `true`, if successful
+@see init_pair(3x)
+@see pair_content
+*/
+static int
+Pinit_pair(lua_State *L)
+{
+	short pair = checkint(L, 1);
+	short f = checkint(L, 2);
+	short b = checkint(L, 3);
+	return pushokresult(init_pair(pair, f, b));
+}
+
+
+/***
+Return the foreground and background colors associated with a color pair id.
+@function pair_content
+@int pair color pair id to act on
+@treturn int foreground color of *pair*
+@treturn int background color of *pair*
+@see can_change_color(3x)
+@see init_pair
+*/
+static int
+Ppair_content(lua_State *L)
+{
+	short pair = checkint(L, 1);
+	short f;
+	short b;
+	int ret = pair_content(pair, &f, &b);
+
+	if (ret == ERR)
+		return 0;
+
+	lua_pushinteger(L, f);
+	lua_pushinteger(L, b);
+	return 2;
+}
+
+
+/***
+How many colors are available for this terminal?
+@function colors
+@treturn int total number of available colors
+@see can_change_color(3x)
+@see color_pairs
+*/
+static int
+Pcolors(lua_State *L)
+{
+	return pushintresult(COLORS);
+}
+
+
+/***
+How may distinct color pairs are supported by this terminal?
+@function color_pairs
+@treturn int total number of available color pairs
+@see can_change_color(3x)
+@see colors
+*/
+static int
+Pcolor_pairs(lua_State *L)
+{
+	return pushintresult(COLOR_PAIRS);
+}
+
+
+/***
+Return the attributes for the given color pair id.
+@function color_pair
+@int pair color pair id to act on
+@treturn int attributes for color pair *pair*
+@see can_change_color(3x)
+*/
+static int
+Pcolor_pair(lua_State *L)
+{
+	int n = checkint(L, 1);
+	return pushintresult(COLOR_PAIR(n));
+}
+
+
+/***
+Fetch the output speed of the terminal.
+@function baudrate
+@treturn int output speed of the terminal in bits-per-second
+@see baudrate(3x)
+*/
+static int
+Pbaudrate(lua_State *L)
+{
+	return pushintresult(baudrate());
+}
+
+
+/***
+Fetch the terminal's current erase character.
+@function erasechar
+@treturn int current erase character
+@see erasechar(3x)
+*/
+static int
+Perasechar(lua_State *L)
+{
+	return pushintresult(erasechar());
+}
+
+
+/***
+Fetch the character insert and delete capability of the terminal.
+@function has_ic
+@treturn bool `true`, if the terminal has insert and delete character
+  operations
+@see has_ic(3x)
+*/
+static int
+Phas_ic(lua_State *L)
+{
+	return pushboolresult(has_ic());
+}
+
+
+/***
+Fetch the line insert and delete capability of the terminal.
+@function has_il
+@treturn bool `true`, if the terminal has insert and delete line operations
+@see has_il(3x)
+*/
+static int
+Phas_il(lua_State *L)
+{
+	return pushboolresult(has_il());
+}
+
+
+/***
+Fetch the terminal's current kill character.
+@function killchar
+@treturn int current line kill character
+@see killchar(3x)
+*/
+static int
+Pkillchar(lua_State *L)
+{
+	return pushintresult(killchar());
+}
+
+
+/***
+Bitwise OR of all (or selected) video attributes supported by the terminal.
+@function termattrs
+@int[opt] a terminal attribute bits
+@treturn[1] bool `true`, if the terminal supports attribute *a*
+@treturn[2] int bitarray of supported terminal attributes
+@see termattrs(3x)
+*/
+static int
+Ptermattrs(lua_State *L)
+{
+	if (lua_gettop(L) > 0)
+	{
+		int a = checkint(L, 1);
+		return pushboolresult(termattrs() & a);
+	}
+	return pushintresult(termattrs());
+}
+
+
+/***
+Fetch the name of the terminal.
+@function termname
+@treturn string terminal name
+@see termname(3x)
+*/
+static int
+Ptermname(lua_State *L)
+{
+	return pushstringresult(termname());
+}
+
+
+/***
+Fetch the verbose name of the terminal.
+@function longname
+@treturn string verbose description of the current terminal
+@see longname(3x)
+*/
+static int
+Plongname(lua_State *L)
+{
+	return pushstringresult(longname());
+}
+
+
+/* there is no easy way to implement this... */
+
+static lua_State *rip_L = NULL;
+
+static int
+ripoffline_cb(WINDOW* w, int cols)
+{
+	static int line = 0;
+	int top = lua_gettop(rip_L);
+
+	/* better be safe */
+	if (!lua_checkstack(rip_L, 5))
+		return 0;
+
+	/* get the table from the registry */
+	lua_pushstring(rip_L, RIPOFF_TABLE);
+	lua_gettable(rip_L, LUA_REGISTRYINDEX);
+
+	/* get user callback function */
+	if (lua_isnil(rip_L, -1)) {
+		lua_pop(rip_L, 1);
+		return 0;
+	}
+
+	lua_rawgeti(rip_L, -1, ++line);	/* function to be called */
+	lc_newwin(rip_L, w);		/* create window object */
+	lua_pushinteger(rip_L, cols);   /* push number of columns */
+
+	lua_pcall(rip_L, 2,  0, 0);	/* call the lua function */
+
+	lua_settop(rip_L, top);
+	return 1;
+}
+
+
+/***
+Reduce the available size of the main screen.
+@function ripoffline
+@bool top_line
+@func callback
+@treturn bool `true`, if successful
+@see ripoffline(3x)
+*/
+static int
+Pripoffline(lua_State *L)
+{
+	static int rip = 0;
+	int top_line = lua_toboolean(L, 1);
+
+#if LCURSES_POSIX_COMPLIANT
+	if (!lua_isfunction(L, 2))
+	{
+		lua_pushliteral(L, "invalid callback passed as second parameter");
+		lua_error(L);
+	}
+
+	/* need to save the lua state somewhere... */
+	rip_L = L;
+
+	/* get the table where we are going to save the callbacks */
+	lua_pushstring(L, RIPOFF_TABLE);
+	lua_gettable(L, LUA_REGISTRYINDEX);
+
+	if (lua_isnil(L, -1))
+	{
+		lua_pop(L, 1);
+		lua_newtable(L);
+
+		lua_pushstring(L, RIPOFF_TABLE);
+		lua_pushvalue(L, -2);
+		lua_settable(L, LUA_REGISTRYINDEX);
+	}
+
+	/* save function callback in registry table */
+	lua_pushvalue(L, 2);
+	lua_rawseti(L, -2, ++rip);
+
+	/* and tell curses we are going to take the line */
+	return pushokresult(ripoffline(top_line ? 1 : -1, ripoffline_cb));
+#else
+	return binding_notimplemented(L, "ripoffline", "curses");
+#endif
+}
+
+
+/***
+Change the visibility of the cursor.
+@function curs_set
+@int vis one of `0` (invisible), `1` (visible) or `2` (very visible)
+@treturn[1] int previous cursor state
+@return[2] nil if *vis* is not supported
+@see curs_set(3x)
+*/
+static int
+Pcurs_set(lua_State *L)
+{
+	int vis = checkint(L, 1);
+	int state = curs_set(vis);
+	if (state == ERR)
+		return 0;
+
+	return pushintresult(state);
+}
+
+
+/***
+Sleep for a few milliseconds.
+@function napms
+@int ms time to wait in milliseconds
+@treturn bool `true`, if successful
+@see napms(3x)
+@see delay_output
+*/
+static int
+Pnapms(lua_State *L)
+{
+	int ms = checkint(L, 1);
+	return pushokresult(napms(ms));
+}
+
+
+/***
+Change the terminal size.
+@function resizeterm
+@int nlines number of lines
+@int ncols number of columns
+@treturn bool `true`, if successful
+@raise unimplemented
+@fixme ncurses only?
+*/
+static int
+Presizeterm(lua_State *L)
+{
+	int nlines  = checkint(L, 1);
+	int ncols   = checkint(L, 2);
+	return pushokresult(resizeterm (nlines, ncols));
+}
+
+
+/***
+Send the terminal audible bell.
+@function beep
+@treturn bool `true`, if successful
+@see beep(3x)
+@see flash
+*/
+static int
+Pbeep(lua_State *L)
+{
+	return pushokresult(beep());
+}
+
+
+/***
+Send the terminal visible bell.
+@function flash
+@treturn bool `true`, if successful
+@see flash(3x)
+@see beep
+*/
+static int
+Pflash(lua_State *L)
+{
+	return pushokresult(flash());
+}
+
+
+/***
+Create a new window.
+@function newwin
+@int nlines number of window lines
+@int ncols number of window columns
+@int begin_y top line of window
+@int begin_x leftmost column of window
+@treturn window a new window object
+@see newwin(3x)
+@see curses.window
+*/
+static int
+Pnewwin(lua_State *L)
+{
+	int nlines  = checkint(L, 1);
+	int ncols   = checkint(L, 2);
+	int begin_y = checkint(L, 3);
+	int begin_x = checkint(L, 4);
+
+	lc_newwin(L, newwin(nlines, ncols, begin_y, begin_x));
+	return 1;
+}
+
+
+/***
+Refresh the visible terminal screen.
+@function doupdate
+@treturn bool `true`, if successful
+@see doupdate(3x)
+@see curses.window:refresh
+*/
+static int
+Pdoupdate(lua_State *L)
+{
+	return pushokresult(doupdate());
+}
+
+
+/***
+Initialise the soft label keys area.
+This must be called before @{initscr}.
+@function slk_init
+@int fmt
+@treturn bool `true`, if successful
+@see slk_init(3x)
+*/
+static int
+Pslk_init(lua_State *L)
+{
+	int fmt = checkint(L, 1);
+#if LCURSES_POSIX_COMPLIANT
+	return pushokresult(slk_init(fmt));
+#else
+	return binding_notimplemented(L, "slk_init", "curses");
+#endif
+}
+
+
+/***
+Set the label for a soft label key.
+@function slk_set
+@int labnum
+@string label
+@int fmt
+@treturn bool `true`, if successful
+@see slk_set(3x)
+*/
+static int
+Pslk_set(lua_State *L)
+{
+	int labnum = checkint(L, 1);
+	const char* label = luaL_checkstring(L, 2);
+	int fmt = checkint(L, 3);
+#if LCURSES_POSIX_COMPLIANT
+	return pushokresult(slk_set(labnum, label, fmt));
+#else
+	return binding_notimplemented(L, "slk_set", "curses");
+#endif
+}
+
+
+/***
+Refresh the soft label key area.
+@function slk_refresh
+@treturn bool `true`, if successful
+@see slk_refresh(3x)
+@see curses.window:refresh
+*/
+static int
+Pslk_refresh(lua_State *L)
+{
+#if LCURSES_POSIX_COMPLIANT
+	return pushokresult(slk_refresh());
+#else
+	return binding_notimplemented(L, "slk_refresh", "curses");
+#endif
+}
+
+
+/***
+Copy the soft label key area backing screen to the virtual screen.
+@function slk_noutrefresh
+@treturn bool `true`, if successful
+@see slk_noutrefresh(3x)
+@see curses.window:refresh
+*/
+static int
+Pslk_noutrefresh(lua_State *L)
+{
+#if LCURSES_POSIX_COMPLIANT
+	return pushokresult(slk_noutrefresh());
+#else
+	return binding_notimplemented(L, "slk_noutrefresh", "curses");
+#endif
+}
+
+
+/***
+Fetch the label for a soft label key.
+@function slk_label
+@int labnum
+@treturn string current label for *labnum*
+@see slk_label(3x)
+*/
+static int
+Pslk_label(lua_State *L)
+{
+	int labnum = checkint(L, 1);
+#if LCURSES_POSIX_COMPLIANT
+	return pushstringresult(slk_label(labnum));
+#else
+	return binding_notimplemented(L, "slk_label", "curses");
+#endif
+}
+
+
+/***
+Clears the soft labels from the screen.
+@function slk_clear
+@treturn bool `true`, if successful
+@see slk_clear(3x)
+@see slk_restore
+*/
+static int
+Pslk_clear(lua_State *L)
+{
+#if LCURSES_POSIX_COMPLIANT
+	return pushokresult(slk_clear());
+#else
+	return binding_notimplemented(L, "slk_clear", "curses");
+#endif
+}
+
+
+/***
+Restores the soft labels to the screen.
+@function slk_restore
+@treturn bool `true`, if successful
+@see slk_restore(3x)
+@see slk_clear
+*/
+static int
+Pslk_restore(lua_State *L)
+{
+#if LCURSES_POSIX_COMPLIANT
+	return pushokresult(slk_restore());
+#else
+	return binding_notimplemented(L, "slk_restore", "curses");
+#endif
+}
+
+
+/***
+Mark the soft label key area for refresh.
+@function slk_touch
+@treturn bool `true`, if successful
+@see slk_touch(3x)
+*/
+static int
+Pslk_touch(lua_State *L)
+{
+#if LCURSES_POSIX_COMPLIANT
+	return pushokresult(slk_touch());
+#else
+	return binding_notimplemented(L, "slk_touch", "curses");
+#endif
+}
+
+
+/***
+Enable an attribute for soft labels.
+@function slk_attron
+@int attrs
+@treturn bool `true`, if successful
+@see slk_attron(3x)
+*/
+static int
+Pslk_attron(lua_State *L)
+{
+	chtype attrs = checkch(L, 1);
+#if LCURSES_POSIX_COMPLIANT
+	return pushokresult(slk_attron(attrs));
+#else
+	return binding_notimplemented(L, "slk_attron", "curses");
+#endif
+}
+
+
+/***
+Disable an attribute for soft labels.
+@function slk_attroff
+@int attrs
+@treturn bool `true`, if successful
+@see slk_attroff(3x)
+*/
+static int
+Pslk_attroff(lua_State *L)
+{
+	chtype attrs = checkch(L, 1);
+#if LCURSES_POSIX_COMPLIANT
+	return pushokresult(slk_attroff(attrs));
+#else
+	return binding_notimplemented(L, "slk_attroff", "curses");
+#endif
+}
+
+
+/***
+Set the attributes for soft labels.
+@function slk_attrset
+@int attrs
+@treturn bool `true`, if successful
+@see slk_attrset(3x)
+*/
+static int
+Pslk_attrset(lua_State *L)
+{
+	chtype attrs = checkch(L, 1);
+#if LCURSES_POSIX_COMPLIANT
+	return pushokresult(slk_attrset(attrs));
+#else
+	return binding_notimplemented(L, "slk_attrset", "curses");
+#endif
+}
+
+
+/***
+Put the terminal into cbreak mode.
+@function cbreak
+@bool[opt] on
+@treturn bool `true`, if successful
+@see cbreak(3x)
+@see nocbreak(3x)
+*/
+static int
+Pcbreak(lua_State *L)
+{
+	if (lua_isnoneornil(L, 1) || lua_toboolean(L, 1))
+		return pushokresult(cbreak());
+	return pushokresult(nocbreak());
+}
+
+
+/***
+Whether characters are echoed to the terminal as they are typed.
+@function echo
+@bool[opt] on
+@treturn bool `true`, if successful
+@see echo(3x)
+@see noecho(3x)
+*/
+static int
+Pecho(lua_State *L)
+{
+	if (lua_isnoneornil(L, 1) || lua_toboolean(L, 1))
+		return pushokresult(echo());
+	return pushokresult(noecho());
+}
+
+
+/***
+Put the terminal into raw mode.
+@function raw
+@bool[opt] on
+@treturn bool `true`, if successful
+@see noraw(3x)
+@see raw(3x)
+*/
+static int
+Praw(lua_State *L)
+{
+	if (lua_isnoneornil(L, 1) || lua_toboolean(L, 1))
+		return pushokresult(raw());
+	return pushokresult(noraw());
+}
+
+
+/***
+Put the terminal into halfdelay mode.
+@function halfdelay
+@int tenths delay in tenths of a second
+@treturn bool `true`, if successful
+@see halfdelay(3x)
+*/
+static int
+Phalfdelay(lua_State *L)
+{
+	int tenths = checkint(L, 1);
+	return pushokresult(halfdelay(tenths));
+}
+
+
+/***
+Whether to translate a return key to newline on input.
+@function nl
+@bool[opt] on
+@treturn bool `true`, if successful
+@see nl(3x)
+@see nonl(3x)
+*/
+static int
+Pnl(lua_State *L)
+{
+	if (lua_isnoneornil(L, 1) || lua_toboolean(L, 1))
+		return pushokresult(nl());
+	return pushokresult(nonl());
+}
+
+
+/***
+Return a printable representation of a character, ignoring attributes.
+@function unctrl
+@int c character to act on
+@treturn string printable representation of *c*
+@see unctrl(3x)
+@see keyname
+*/
+static int
+Punctrl(lua_State *L)
+{
+	chtype c = checkch(L, 1);
+	return pushstringresult(unctrl(c));
+}
+
+
+/***
+Return a printable representation of a key.
+@function keyname
+@int c a key
+@treturn string key name of *c*
+@see keyname(3x)
+@see unctrl
+*/
+static int
+Pkeyname(lua_State *L)
+{
+	int c = checkint(L, 1);
+	return pushstringresult(keyname(c));
+}
+
+
+/***
+Insert padding characters to force a short delay.
+@function delay_output
+@int ms delay time in milliseconds
+@treturn bool `true`, if successful
+@see napms
+@fixme ncurses only?
+*/
+static int
+Pdelay_output(lua_State *L)
+{
+	int ms = checkint(L, 1);
+	return pushokresult(delay_output(ms));
+}
+
+
+/***
+Throw away any typeahead in the keyboard input buffer.
+@function flushinp
+@treturn bool `true`, if successful
+@see flushinp(3x)
+@see ungetch
+*/
+static int
+Pflushinp(lua_State *L)
+{
+	return pushboolresult(flushinp());
+}
+
+
+/***
+Return a character to the keyboard input buffer.
+@function ungetch
+@int c
+@treturn bool `true`, if successful
+@see ungetch(3x)
+@see flushinp
+*/
+static int
+Pungetch(lua_State *L)
+{
+	int c = checkint(L, 1);
+	return pushokresult(ungetch(c));
+}
+
+
+/***
+Create a new pad.
+@function newpad
+@int nlines
+@int ncols
+@treturn bool `true`, if successful
+@see newpad(3x)
+*/
+static int
+Pnewpad(lua_State *L)
+{
+	int nlines = checkint(L, 1);
+	int ncols = checkint(L, 2);
+	lc_newwin(L, newpad(nlines, ncols));
+	return 1;
+}
+
+
+static char ti_capname[32];
+
+/***
+Fetch terminfo boolean capability.
+@function tigetflag
+@string capname
+@treturn bool content of terminal boolean capability
+@see tigetflag(3x)
+@see terminfo(5)
+*/
+static int
+Ptigetflag (lua_State *L)
+{
+	int r;
+
+	strlcpy (ti_capname, luaL_checkstring (L, 1), sizeof (ti_capname));
+	r = tigetflag (ti_capname);
+	if (-1 == r)
+		return luaL_error (L, "`%s' is not a boolean capability", ti_capname);
+	return pushboolresult (r);
+}
+
+
+/***
+Fetch terminfo numeric capability.
+@function tigetnum
+@string capname
+@treturn int content of terminal numeric capability
+@see tigetnum(3x)
+@see terminfo(5)
+*/
+static int
+Ptigetnum (lua_State *L)
+{
+	int res;
+
+	strlcpy (ti_capname, luaL_checkstring (L, 1), sizeof (ti_capname));
+	res = tigetnum (ti_capname);
+	if (-2 == res)
+		return luaL_error (L, "`%s' is not a numeric capability", ti_capname);
+	else if (-1 == res)
+		lua_pushnil (L);
+	else
+		lua_pushinteger(L, res);
+	return 1;
+}
+
+
+/***
+Fetch terminfo string capability.
+@function tigetstr
+@string capname
+@treturn string content of terminal string capability
+@see tigetstr(3x)
+@see terminfo(5)
+*/
+static int
+Ptigetstr (lua_State *L)
+{
+	const char *res;
+
+	strlcpy (ti_capname, luaL_checkstring (L, 1), sizeof (ti_capname));
+	res = tigetstr (ti_capname);
+	if ((char *) -1 == res)
+		return luaL_error (L, "`%s' is not a string capability", ti_capname);
+	else if (NULL == res)
+		lua_pushnil (L);
+	else
+		lua_pushstring(L, res);
+	return 1;
+}
+
+
+static const luaL_Reg curseslib[] =
+{
+	LCURSES_FUNC( Pbaudrate		),
+	LCURSES_FUNC( Pbeep		),
+	LCURSES_FUNC( Pcbreak		),
+	LCURSES_FUNC( Pcolor_pair	),
+	LCURSES_FUNC( Pcolor_pairs	),
+	LCURSES_FUNC( Pcolors		),
+	LCURSES_FUNC( Pcols		),
+	LCURSES_FUNC( Pcurs_set		),
+	LCURSES_FUNC( Pdelay_output	),
+	LCURSES_FUNC( Pdoupdate		),
+	LCURSES_FUNC( Pecho		),
+	LCURSES_FUNC( Pendwin		),
+	LCURSES_FUNC( Perasechar	),
+	LCURSES_FUNC( Pflash		),
+	LCURSES_FUNC( Pflushinp		),
+	LCURSES_FUNC( Phalfdelay	),
+	LCURSES_FUNC( Phas_colors	),
+	LCURSES_FUNC( Phas_ic		),
+	LCURSES_FUNC( Phas_il		),
+	LCURSES_FUNC( Pinit_pair	),
+	LCURSES_FUNC( Pisendwin		),
+	LCURSES_FUNC( Pkeyname		),
+	LCURSES_FUNC( Pkillchar		),
+	LCURSES_FUNC( Plines		),
+	LCURSES_FUNC( Plongname		),
+	LCURSES_FUNC( Pnapms		),
+	LCURSES_FUNC( Pnew_chstr	),
+	LCURSES_FUNC( Pnewpad		),
+	LCURSES_FUNC( Pnewwin		),
+	LCURSES_FUNC( Pnl		),
+	LCURSES_FUNC( Ppair_content	),
+	LCURSES_FUNC( Praw		),
+	LCURSES_FUNC( Presizeterm	),
+	LCURSES_FUNC( Pripoffline	),
+	LCURSES_FUNC( Pslk_attroff	),
+	LCURSES_FUNC( Pslk_attron	),
+	LCURSES_FUNC( Pslk_attrset	),
+	LCURSES_FUNC( Pslk_clear	),
+	LCURSES_FUNC( Pslk_init		),
+	LCURSES_FUNC( Pslk_label	),
+	LCURSES_FUNC( Pslk_noutrefresh	),
+	LCURSES_FUNC( Pslk_refresh	),
+	LCURSES_FUNC( Pslk_restore	),
+	LCURSES_FUNC( Pslk_set		),
+	LCURSES_FUNC( Pslk_touch	),
+	LCURSES_FUNC( Pstart_color	),
+	LCURSES_FUNC( Pstdscr		),
+	LCURSES_FUNC( Ptermattrs	),
+	LCURSES_FUNC( Ptermname		),
+	LCURSES_FUNC( Ptigetflag	),
+	LCURSES_FUNC( Ptigetnum		),
+	LCURSES_FUNC( Ptigetstr		),
+	LCURSES_FUNC( Punctrl		),
+	LCURSES_FUNC( Pungetch		),
+	LCURSES_FUNC( Puse_default_colors),
+	{NULL, NULL}
+};
+
+/***
+Constants.
+@section constants
+*/
+
+/***
+Curses constants.
+Any constants not available in the underlying system will be `nil` valued,
+see @{curses.lua}. Many of the `KEY_` constants cannot be generated by
+modern keyboards and are mostly for historical compatibility with ancient
+terminal hardware keyboards.
+
+Note that almost all of these constants remain undefined (`nil`) until
+after @{curses.initscr} has returned successfully.
+@table curses
+@int ACS_BLOCK alternate character set solid block
+@int ACS_BOARD alternate character set board of squares
+@int ACS_BTEE alternate character set bottom-tee
+@int ACS_BULLET alternate character set bullet
+@int ACS_CKBOARD alternate character set stipple
+@int ACS_DARROW alternate character set down arrow
+@int ACS_DEGREE alternate character set degrees mark
+@int ACS_DIAMOND alternate character set diamond
+@int ACS_HLINE alternate character set horizontal line
+@int ACS_LANTERN alternate character set lantern
+@int ACS_LARROW alternate character set left arrow
+@int ACS_LLCORNER alternate character set lower left corner
+@int ACS_LRCORNER alternate character set lower right corner
+@int ACS_LTEE alternate character set left tee
+@int ACS_PLMINUS alternate character set plus/minus
+@int ACS_PLUS alternate character set plus
+@int ACS_RARROW alternate character set right arrow
+@int ACS_RTEE alternate character set right tee
+@int ACS_S1 alternate character set scan-line 1
+@int ACS_S9 alternate character set scan-line 9
+@int ACS_TTEE alternate character set top tee
+@int ACS_UARROW alternate character set up arrow
+@int ACS_ULCORNER alternate character set upper left corner
+@int ACS_URCORNER alternate character set upper right corner
+@int ACS_VLINE alternate character set vertical line
+@int A_ALTCHARSET alternatate character set attribute
+@int A_ATTRIBUTES attributed character attributes bitmask
+@int A_BLINK blinking attribute
+@int A_BOLD bold attribute
+@int A_CHARTEXT attributed character text bitmask
+@int A_COLOR attributed character color-pair bitmask
+@int A_DIM half-bright attribute
+@int A_INVIS invisible attribute
+@int A_NORMAL normal attribute (all attributes off)
+@int A_PROTECT protected attribute
+@int A_REVERSE reverse video attribute
+@int A_STANDOUT standout attribute
+@int A_UNDERLINE underline attribute
+@int COLOR_BLACK black color attribute
+@int COLOR_BLUE blue color attribute
+@int COLOR_CYAN cyan color attribute
+@int COLOR_GREEN green color attribute
+@int COLOR_MAGENTA magenta color attribute
+@int COLOR_RED red color attribute
+@int COLOR_WHITE white color attribute
+@int COLOR_YELLOW yellow color attribute
+@int KEY_A1 upper-left of keypad key
+@int KEY_A3 upper-right of keypad key
+@int KEY_B2 center of keypad key
+@int KEY_BACKSPACE backspace key
+@int KEY_BEG beginning key
+@int KEY_BREAK break key
+@int KEY_BTAB backtab key
+@int KEY_C1 bottom-left of keypad key
+@int KEY_C3 bottom-right of keypad key
+@int KEY_CANCEL cancel key
+@int KEY_CATAB clear all tabs key
+@int KEY_CLEAR clear screen key
+@int KEY_CLOSE close key
+@int KEY_COMMAND command key
+@int KEY_COPY copy key
+@int KEY_CREATE create key
+@int KEY_CTAB clear tab key
+@int KEY_DC delete character key
+@int KEY_DL delete line key
+@int KEY_DOWN down arrow key
+@int KEY_EIC exit insert char mode key
+@int KEY_END end key
+@int KEY_ENTER enter key
+@int KEY_EOL clear to end of line key
+@int KEY_EOS clear to end of screen key
+@int KEY_EXIT exit key
+@int KEY_F0 f0 key
+@int KEY_F1 f1 key
+@int KEY_F2 f2 key
+@int KEY_F3 f3 key
+@int KEY_F4 f4 key
+@int KEY_F5 f5 key
+@int KEY_F6 f6 key
+@int KEY_F7 f7 key
+@int KEY_F8 f8 key
+@int KEY_F9 f9 key
+@int KEY_F10 f10 key
+@int KEY_F11 f11 key
+@int KEY_F12 f12 key
+@int KEY_F13 f13 key
+@int KEY_F14 f14 key
+@int KEY_F15 f15 key
+@int KEY_F16 f16 key
+@int KEY_F17 f17 key
+@int KEY_F18 f18 key
+@int KEY_F19 f19 key
+@int KEY_F20 f20 key
+@int KEY_F21 f21 key
+@int KEY_F22 f22 key
+@int KEY_F23 f23 key
+@int KEY_F24 f24 key
+@int KEY_F25 f25 key
+@int KEY_F26 f26 key
+@int KEY_F27 f27 key
+@int KEY_F28 f28 key
+@int KEY_F29 f29 key
+@int KEY_F30 f30 key
+@int KEY_F31 f31 key
+@int KEY_F32 f32 key
+@int KEY_F33 f33 key
+@int KEY_F34 f34 key
+@int KEY_F35 f35 key
+@int KEY_F36 f36 key
+@int KEY_F37 f37 key
+@int KEY_F38 f38 key
+@int KEY_F39 f39 key
+@int KEY_F40 f40 key
+@int KEY_F41 f41 key
+@int KEY_F42 f42 key
+@int KEY_F43 f43 key
+@int KEY_F44 f44 key
+@int KEY_F45 f45 key
+@int KEY_F46 f46 key
+@int KEY_F47 f47 key
+@int KEY_F48 f48 key
+@int KEY_F49 f49 key
+@int KEY_F50 f50 key
+@int KEY_F51 f51 key
+@int KEY_F52 f52 key
+@int KEY_F53 f53 key
+@int KEY_F54 f54 key
+@int KEY_F55 f55 key
+@int KEY_F56 f56 key
+@int KEY_F57 f57 key
+@int KEY_F58 f58 key
+@int KEY_F59 f59 key
+@int KEY_F60 f60 key
+@int KEY_F61 f61 key
+@int KEY_F62 f62 key
+@int KEY_F63 f63 key
+@int KEY_FIND find key
+@int KEY_HELP help key
+@int KEY_HOME home key
+@int KEY_IC enter insert char mode key
+@int KEY_IL insert line key
+@int KEY_LEFT cursor left key
+@int KEY_LL home down or bottom key
+@int KEY_MARK mark key
+@int KEY_MESSAGE message key
+@int KEY_MOUSE mouse event available virtual key
+@int KEY_MOVE move key
+@int KEY_NEXT next object key
+@int KEY_NPAGE next page key
+@int KEY_OPEN open key
+@int KEY_OPTIONS options key
+@int KEY_PPAGE previous page key
+@int KEY_PREVIOUS prewious object key
+@int KEY_PRINT print key
+@int KEY_REDO redo key
+@int KEY_REFERENCE reference key
+@int KEY_REFRESH refresh key
+@int KEY_REPLACE replace key
+@int KEY_RESET hard reset key
+@int KEY_RESIZE resize event virtual key
+@int KEY_RESTART restart key
+@int KEY_RESUME resume key
+@int KEY_RIGHT cursor right key
+@int KEY_SAVE save key
+@int KEY_SBEG shift beginning key
+@int KEY_SCANCEL shift cancel key
+@int KEY_SCOMMAND shift command key
+@int KEY_SCOPY shift copy key
+@int KEY_SCREATE shift create key
+@int KEY_SDC shift delete character key
+@int KEY_SDL shift delete line key
+@int KEY_SELECT select key
+@int KEY_SEND send key
+@int KEY_SEOL shift clear to end of line key
+@int KEY_SEXIT shift exit key
+@int KEY_SF scroll one line forward key
+@int KEY_SFIND shift find key
+@int KEY_SHELP shift help key
+@int KEY_SHOME shift home key
+@int KEY_SIC shift enter insert mode key
+@int KEY_SLEFT shift cursor left key
+@int KEY_SMESSAGE shift message key
+@int KEY_SMOVE shift move key
+@int KEY_SNEXT shift next object key
+@int KEY_SOPTIONS shift options key
+@int KEY_SPREVIOUS shift previous object key
+@int KEY_SPRINT shift print key
+@int KEY_SR scroll one line backward key
+@int KEY_SREDO shift redo key
+@int KEY_SREPLACE shift replace key
+@int KEY_SRESET soft reset key
+@int KEY_SRIGHT shift cursor right key
+@int KEY_SRSUME shift resume key
+@int KEY_SSAVE shift save key
+@int KEY_SSUSPEND shift suspend key
+@int KEY_STAB shift tab key
+@int KEY_SUNDO shift undo key
+@int KEY_SUSPEND suspend key
+@int KEY_UNDO undo key
+@int KEY_UP cursor up key
+@usage
+  -- Print curses constants supported on this host.
+  for name, value in pairs (require "curses") do
+    if type (value) == "number" then
+      print (name, value)
+     end
+  end
+*/
+
+LUALIB_API int
+luaopen_curses_c(lua_State *L)
+{
+	luaopen_curses_window(L);
+	luaopen_curses_chstr(L);
+
+	luaL_register(L, "curses", curseslib);
+
+	lua_pushstring(L, "initscr");
+	lua_pushvalue(L, -2);
+	lua_pushcclosure(L, Pinitscr, 1);
+	lua_settable(L, -3);
+
+	return 1;
+}
diff --git a/src/lcurses/curses.lua b/src/lcurses/curses.lua
new file mode 100644
index 0000000..bba9d12
--- /dev/null
+++ b/src/lcurses/curses.lua
@@ -0,0 +1,58 @@
+--- Lua bindings for curses
+local M = curses
+
+-- These Lua functions detect number of args, like Unified Funcs in Perl Curses
+-- see http://pjb.com.au/comp/lua/lcurses.html
+-- see http://search.cpan.org/perldoc?Curses
+
+function M.addch (...)
+  if #{...} == 3 then
+    return curses.stdscr():mvaddch(...)
+  else
+    return curses.stdscr():addch(...)
+  end
+end
+
+function M.addstr(...) -- detect number of args, like Unified Funcs in Perl Curses
+  if #{...} == 3 then
+    return curses.stdscr():mvaddstr(...)
+  else
+    return curses.stdscr():addstr(...)
+  end
+end
+
+function M.attrset (a) return curses.stdscr():attrset(a) end
+function M.clear ()    return curses.stdscr():clear() end
+function M.clrtobot () return curses.stdscr():clrtobot() end
+function M.clrtoeol () return curses.stdscr():clrtoeol() end
+
+function M.getch (...)
+  local c
+  if #{...} == 2 then
+    c = curses.stdscr():mvgetch(...)
+  else
+    c = curses.stdscr():getch()
+  end
+  if c < 256 then
+    return string.char(c)
+  end
+  -- could kludge-test for utf8, e.g. c3 a9 20  c3 aa 20  c3 ab 20  e2 82 ac 0a
+  return c
+end
+
+function M.getstr (...)
+  if #{...} > 1 then
+    return curses.stdscr():mvgetstr(...)
+  else
+    return curses.stdscr():getstr(...)
+  end
+end
+M.getnstr = M.getstr
+
+function M.getyx ()    return curses.stdscr():getyx() end
+function M.keypad (b)  return curses.stdscr():keypad(b) end
+function M.move (y,x)  return curses.stdscr():move(y,x) end
+function M.refresh ()  return curses.stdscr():refresh() end
+function M.timeout (t) return curses.stdscr():timeout(t) end
+
+return M
diff --git a/src/lcurses/strlcpy.c b/src/lcurses/strlcpy.c
new file mode 100644
index 0000000..e038cd4
--- /dev/null
+++ b/src/lcurses/strlcpy.c
@@ -0,0 +1,76 @@
+/*	$OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $	*/
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef LCURSES_STRLCPY_C
+#define LCURSES_STRLCPY_C 1
+
+#ifndef HAVE_STRLCPY
+
+#if defined LIBC_SCCS && ! defined lint
+static char *rcsid = "$OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <string.h>
+
+static size_t strlcpy(char *dst, const char *src, size_t siz);
+
+/*
+ * Copy src to string dst of size siz.  At most siz-1 characters
+ * will be copied.  Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+static size_t strlcpy(char *dst, const char *src, size_t siz)
+{
+        register char *d = dst;
+        register const char *s = src;
+        register size_t n = siz;
+
+        /* Copy as many bytes as will fit */
+        if (n != 0 && --n != 0) {
+                do {
+                        if ((*d++ = *s++) == 0)
+                                break;
+                } while (--n != 0);
+        }
+
+        /* Not enough room in dst, add NUL and traverse rest of src */
+        if (n == 0) {
+                if (siz != 0)
+                        *d = '\0';		/* NUL-terminate dst */
+                while (*s++)
+                        ;
+        }
+
+        return(s - src - 1);	/* count does not include NUL */
+}
+
+#endif /* !HAVE_STRLCPY */
+
+#endif /* !LCURSES_STRLCPY_C */
diff --git a/src/lcurses/window.c b/src/lcurses/window.c
new file mode 100644
index 0000000..fd18570
--- /dev/null
+++ b/src/lcurses/window.c
@@ -0,0 +1,1940 @@
+/*
+ * Curses binding for Lua 5.1, 5.2 & 5.3.
+ *
+ * (c) Gary V. Vaughan <gary@vaughan.pe> 2013-2017
+ * (c) Reuben Thomas <rrt@sc3d.org> 2009-2012
+ * (c) Tiago Dionizio <tiago.dionizio AT gmail.com> 2004-2007
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/***
+ Curses Window objects.
+
+ The methods documented on this page are available on any Curses Window
+ object, such as created by:
+
+     stdscr = curses.initscr ()
+     window = curses.newwin (25, 80, 0, 0)
+
+@classmod curses.window
+*/
+
+//? #include <config.h>
+
+#include "_helpers.c"
+
+#include "chstr.c"
+
+
+static const char *WINDOWMETA = "curses:window";
+
+static void
+lc_newwin(lua_State *L, WINDOW *nw)
+{
+	if (nw)
+	{
+		WINDOW **w = lua_newuserdata(L, sizeof(WINDOW*));
+		luaL_getmetatable(L, WINDOWMETA);
+		lua_setmetatable(L, -2);
+		*w = nw;
+	}
+	else
+	{
+		lua_pushliteral(L, "failed to create window");
+		lua_error(L);
+	}
+}
+
+
+static WINDOW **
+lc_getwin(lua_State *L, int offset)
+{
+	WINDOW **w = (WINDOW**)luaL_checkudata(L, offset, WINDOWMETA);
+	if (w == NULL)
+		luaL_argerror(L, offset, "bad curses window");
+	return w;
+}
+
+
+static WINDOW *
+checkwin(lua_State *L, int offset)
+{
+	WINDOW **w = lc_getwin(L, offset);
+	if (*w == NULL)
+		luaL_argerror(L, offset, "attempt to use closed curses window");
+	return *w;
+}
+
+
+/***
+Unique string representation of a @{curses.window}.
+@function __tostring
+@treturn string unique string representation of the window object.
+*/
+static int
+W__tostring(lua_State *L)
+{
+	WINDOW **w = lc_getwin(L, 1);
+	char buff[34];
+	if (*w == NULL)
+		strcpy(buff, "closed");
+	else
+		sprintf(buff, "%p", lua_touserdata(L, 1));
+	lua_pushfstring(L, "curses window (%s)", buff);
+	return 1;
+}
+
+
+/***
+Free all the resources associated with a window.
+@function close
+@see delwin(3x)
+*/
+static int
+Wclose(lua_State *L)
+{
+	WINDOW **w = lc_getwin(L, 1);
+	if (*w != NULL && *w != stdscr)
+	{
+		delwin(*w);
+		*w = NULL;
+	}
+	return 0;
+}
+
+
+/***
+Move the position of a window.
+@function move_window
+@int y offset frow top of screen
+@int x offset from left of screen
+@treturn bool `true`, if successful
+@see mvwin(3x)
+*/
+static int
+Wmove_window(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int y = checkint(L, 2);
+	int x = checkint(L, 3);
+	return pushokresult(mvwin(w, y, x));
+}
+
+
+/***
+Create a new subwindow.
+@function sub
+@treturn window a new absolutely positioned subwindow
+@int nlines number of window lines
+@int ncols number of window columns
+@int begin_y top line of window
+@int begin_x leftmost column of window
+@see subwin(3x)
+@see derive
+*/
+static int
+Wsub(lua_State *L)
+{
+	WINDOW *orig = checkwin(L, 1);
+	int nlines  = checkint(L, 2);
+	int ncols   = checkint(L, 3);
+	int begin_y = checkint(L, 4);
+	int begin_x = checkint(L, 5);
+
+	lc_newwin(L, subwin(orig, nlines, ncols, begin_y, begin_x));
+	return 1;
+}
+
+
+/***
+Create a new derived window.
+@function derive
+@int nlines number of window lines
+@int ncols number of window columns
+@int begin_y top line of window
+@int begin_x leftmost column of window
+@treturn window a new relatively positioned subwindow
+@see derwin(3x)
+@see sub
+*/
+static int
+Wderive(lua_State *L)
+{
+	WINDOW *orig = checkwin(L, 1);
+	int nlines  = checkint(L, 2);
+	int ncols   = checkint(L, 3);
+	int begin_y = checkint(L, 4);
+	int begin_x = checkint(L, 5);
+
+	lc_newwin(L, derwin(orig, nlines, ncols, begin_y, begin_x));
+	return 1;
+}
+
+
+/***
+Move the position of a derived window.
+@function move_derived
+@int par_y lines from top of parent window
+@int par_x columns from left of parent window
+@treturn bool `true`, if successful
+@see mvderwin(3x)
+*/
+static int
+Wmove_derived(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int par_y = checkint(L, 2);
+	int par_x = checkint(L, 3);
+	return pushokresult(mvderwin(w, par_y, par_x));
+}
+
+
+/***
+Change the size of a window.
+@function resize
+@int height new number of lines
+@int width new number of columns
+@treturn bool `true`, if successful
+@fixme ncurses only?
+*/
+static int
+Wresize(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int height = checkint(L, 2);
+	int width = checkint(L, 3);
+
+	int c = wresize(w, height, width);
+	if (c == ERR) return 0;
+
+	return pushokresult(true);
+}
+
+
+/***
+Make a duplicate of a window.
+@function clone
+@treturn window a new duplicate of this window
+@see dupwin(3x)
+*/
+static int
+Wclone(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	lc_newwin(L, dupwin(w));
+	return 1;
+}
+
+
+/***
+Mark ancestors of a window for refresh.
+@function syncup
+@see wsyncup(3x)
+*/
+static int
+Wsyncup(lua_State *L)
+{
+	wsyncup(checkwin(L, 1));
+	return 0;
+}
+
+
+/***
+Automatically mark ancestors of a changed window for refresh.
+@function syncok
+@bool bf
+@treturn bool `true`, if successful
+@fixme ncurses only
+*/
+static int
+Wsyncok(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+#if LCURSES_POSIX_COMPLIANT
+	int bf = lua_toboolean(L, 2);
+	return pushokresult(syncok(w, bf));
+#else
+	return binding_notimplemented(L, "syncok", "curses");
+#endif
+}
+
+
+/***
+Mark cursor position of ancestors of a window for refresh.
+@function cursyncup
+@see wcursyncup(3x)
+*/
+static int
+Wcursyncup(lua_State *L)
+{
+	wcursyncup(checkwin(L, 1));
+	return 0;
+}
+
+
+/***
+Mark child windows for refresh.
+@function syncdown
+@see syncdown(3x)
+@see refresh
+*/
+static int
+Wsyncdown(lua_State *L)
+{
+	wsyncdown(checkwin(L, 1));
+	return 0;
+}
+
+
+/***
+Refresh the window terminal display from the virtual screen.
+@function refresh
+@treturn bool `true`, if successful
+@see wrefresh(3x)
+@see curses.doupdate
+@see noutrefresh
+*/
+static int
+Wrefresh(lua_State *L)
+{
+	return pushokresult(wrefresh(checkwin(L, 1)));
+}
+
+
+/***
+Copy the window backing screen to the virtual screen.
+@function noutrefresh
+@treturn bool `true`, if successful
+@see wnoutrefresh(3x)
+@see curses.doupdate
+@see refresh
+*/
+static int
+Wnoutrefresh(lua_State *L)
+{
+	return pushokresult(wnoutrefresh(checkwin(L, 1)));
+}
+
+
+/***
+Mark a window as having corrupted display that needs fully redrawing.
+@function redrawwin
+@treturn bool `true`, if successful
+@see redrawwin(3x)
+@see redrawln
+*/
+static int
+Wredrawwin(lua_State *L)
+{
+	return pushokresult(redrawwin(checkwin(L, 1)));
+}
+
+
+/***
+Mark a range of lines in a window as corrupted and in need of redrawing.
+@function redrawln
+@int beg_line
+@int num_lines
+@treturn bool `true`, if successful
+@see wredrawln(3x)
+*/
+static int
+Wredrawln(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int beg_line = checkint(L, 2);
+	int num_lines = checkint(L, 3);
+	return pushokresult(wredrawln(w, beg_line, num_lines));
+}
+
+
+/***
+Change the cursor position.
+@function move
+@int y
+@int x
+@treturn bool `true`, if successful
+@see wmove(3x)
+*/
+static int
+Wmove(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int y = checkint(L, 2);
+	int x = checkint(L, 3);
+	return pushokresult(wmove(w, y, x));
+}
+
+
+/***
+Scroll the window up *n* lines.
+@function scrl
+@int n
+@treturn bool `true`, if successful
+@see wscrl(3x)
+*/
+static int
+Wscrl(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int n = checkint(L, 2);
+	return pushokresult(wscrl(w, n));
+}
+
+
+/***
+Set the changed state of a window since the last refresh.
+@function touch
+@param[opt] changed
+@treturn bool `true`, if successful
+@see touchwin(3x)
+*/
+static int
+Wtouch(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int changed;
+	if (lua_isnoneornil(L, 2))
+		changed = TRUE;
+	else
+		changed = lua_toboolean(L, 2);
+
+	if (changed)
+		return pushokresult(touchwin(w));
+	return pushokresult(untouchwin(w));
+}
+
+
+/***
+Mark a range of lines as changed since the last refresh.
+@function touchline
+@int y
+@int n
+@param[opt] changed
+@treturn bool `true`, if successful
+@see wtouchln(3x)
+*/
+static int
+Wtouchline(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int y = checkint(L, 2);
+	int n = checkint(L, 3);
+	int changed;
+	if (lua_isnoneornil(L, 4))
+		changed = TRUE;
+	else
+		changed = lua_toboolean(L, 4);
+	return pushokresult(wtouchln(w, y, n, changed));
+}
+
+
+/***
+Has a particular window line changed since the last refresh?
+@function is_linetouched
+@int line
+@treturn bool `true`, if successful
+@see is_linetouched(3x)
+*/
+static int
+Wis_linetouched(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int line = checkint(L, 2);
+	return pushboolresult(is_linetouched(w, line));
+}
+
+
+/***
+Has a window changed since the last refresh?
+@function is_wintouched
+@treturn bool `true`, if successful
+@see is_wintouched(3x)
+*/
+static int
+Wis_wintouched(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	return pushboolresult(is_wintouched(w));
+}
+
+
+/***
+Fetch the cursor position.
+@function getyx
+@treturn int y coordinate of top line
+@treturn int x coordinate of left column
+@see getyx(3x)
+*/
+static int
+Wgetyx(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int y, x;
+	getyx(w, y, x);
+	lua_pushinteger(L, y);
+	lua_pushinteger(L, x);
+	return 2;
+}
+
+
+/***
+Fetch the parent-relative coordinates of a subwindow.
+@function getparyx
+@treturn int y coordinate of top line relative to parent window
+@treturn int x coordinate of left column relative to parent window
+@see getparyx(3x)
+*/
+static int
+Wgetparyx(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int y, x;
+	getparyx(w, y, x);
+	lua_pushinteger(L, y);
+	lua_pushinteger(L, x);
+	return 2;
+}
+
+
+/***
+Fetch the absolute top-left coordinates of a window.
+@function getbegyx
+@treturn int y coordinate of top line
+@treturn int x coordinate of left column
+@treturn bool `true`, if successful
+@see getbegyx(3x)
+*/
+static int
+Wgetbegyx(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int y, x;
+	getbegyx(w, y, x);
+	lua_pushinteger(L, y);
+	lua_pushinteger(L, x);
+	return 2;
+}
+
+
+/***
+Fetch the absolute bottom-right coordinates of a window.
+@function getmaxyx
+@treturn int y coordinate of bottom line
+@treturn int x coordinate of right column
+@treturn bool `true`, if successful
+@see getmaxyx(3x)
+*/
+static int
+Wgetmaxyx(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int y, x;
+	getmaxyx(w, y, x);
+	lua_pushinteger(L, y);
+	lua_pushinteger(L, x);
+	return 2;
+}
+
+
+/***
+Draw a border around a window.
+@function border
+@int[opt] ls
+@int[opt] rs
+@int[opt] ts
+@int[opt] bs
+@int[opt] tl
+@int[opt] tr
+@int[opt] bl
+@int[opt] br
+@treturn bool `true`, if successful
+@see wborder(3x)
+@usage
+  win:border (curses.ACS_VLINE, curses.ACS_VLINE,
+              curses.ACS_HLINE, curses.ACS_HLINE,
+	      curses.ACS_ULCORNER, curses.ACS_URCORNER,
+	      curses.ACS_LLCORNER, curses.ACS_LRCORNER)
+*/
+static int
+Wborder(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	chtype ls = optch(L, 2, 0);
+	chtype rs = optch(L, 3, 0);
+	chtype ts = optch(L, 4, 0);
+	chtype bs = optch(L, 5, 0);
+	chtype tl = optch(L, 6, 0);
+	chtype tr = optch(L, 7, 0);
+	chtype bl = optch(L, 8, 0);
+	chtype br = optch(L, 9, 0);
+
+	return pushokresult(wborder(w, ls, rs, ts, bs, tl, tr, bl, br));
+}
+
+
+/***
+Draw a box around a window.
+@function box
+@int verch
+@int horch
+@treturn bool `true`, if successful
+@see box(3x)
+@see border
+@usage
+  win:box (curses.ACS_VLINE, curses.ACS_HLINE)
+*/
+static int
+Wbox(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	chtype verch = checkch(L, 2);
+	chtype horch = checkch(L, 3);
+
+	return pushokresult(box(w, verch, horch));
+}
+
+
+/***
+Draw a row of characters from the current cursor position.
+@function hline
+@int ch
+@int n
+@treturn bool `true`, if successful
+@see whline(3x)
+@see mvhline
+@see vline
+@usage
+  _, width = win:getmaxyx ()
+  win:hline (curses.ACS_HLINE, width - curs_x)
+*/
+static int
+Whline(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	chtype ch = checkch(L, 2);
+	int n = checkint(L, 3);
+
+	return pushokresult(whline(w, ch, n));
+}
+
+
+/***
+Draw a column of characters from the current cursor position.
+@function vline
+@int ch
+@int n
+@treturn bool `true`, if successful
+@see wvline(3x)
+@see hline
+@see mvvline
+*/
+static int
+Wvline(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	chtype ch = checkch(L, 2);
+	int n = checkint(L, 3);
+
+	return pushokresult(wvline(w, ch, n));
+}
+
+
+/***
+Move the cursor, then draw a row of characters from the new cursor position.
+@function mvhline
+@int y
+@int x
+@int ch
+@int n
+@treturn bool `true`, if successful
+@see mvwhline(3x)
+*/
+static int
+Wmvhline(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int y = checkint(L, 2);
+	int x = checkint(L, 3);
+	chtype ch = checkch(L, 4);
+	int n = checkint(L, 5);
+
+	return pushokresult(mvwhline(w, y, x, ch, n));
+}
+
+
+/***
+Move the cursor, then draw a column of characters from the new cursor position.
+@function mvvline
+@int y
+@int x
+@int ch
+@int n
+@treturn bool `true`, if successful
+@see mvwvline(3x)
+*/
+static int
+Wmvvline(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int y = checkint(L, 2);
+	int x = checkint(L, 3);
+	chtype ch = checkch(L, 4);
+	int n = checkint(L, 5);
+
+	return pushokresult(mvwvline(w, y, x, ch, n));
+}
+
+
+/***
+Write blanks to every character position in the window.
+@function erase
+@treturn bool `true`, if successful
+@see werase(3x)
+*/
+static int
+Werase(lua_State *L)
+{
+	return pushokresult(werase(checkwin(L, 1)));
+}
+
+
+/***
+Call @{erase} and then @{clearok}.
+@function clear
+@treturn bool `true`, if successful
+@see wclear(3x)
+*/
+static int
+Wclear(lua_State *L)
+{
+	return pushokresult(wclear(checkwin(L, 1)));
+}
+
+
+/***
+Write blanks to every character position after the cursor.
+@function clrtobot
+@treturn bool `true`, if successful
+@see wclrtobot(3x)
+*/
+static int
+Wclrtobot(lua_State *L)
+{
+	return pushokresult(wclrtobot(checkwin(L, 1)));
+}
+
+
+/***
+Write blanks from the cursor to the end of the current line.
+@function clrtoeol
+@treturn bool `true`, if successful
+@see wclrtoeol(3x)
+*/
+static int
+Wclrtoeol(lua_State *L)
+{
+	return pushokresult(wclrtoeol(checkwin(L, 1)));
+}
+
+
+/***
+Advance the cursor after writing a character at the old position.
+@function addch
+@int ch
+@treturn bool `true`, if successful
+@see waddch(3x)
+*/
+static int
+Waddch(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	chtype ch = checkch(L, 2);
+	return pushokresult(waddch(w, ch));
+}
+
+
+/***
+Call @{move}, then @{addch}.
+@function mvaddch
+@int y
+@int x
+@int ch
+@treturn bool `true`, if successful
+@see mvwaddch(3x)
+*/
+static int
+Wmvaddch(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int y = checkint(L, 2);
+	int x = checkint(L, 3);
+	chtype ch = checkch(L, 4);
+	return pushokresult(mvwaddch(w, y, x, ch));
+}
+
+
+/***
+Call @{addch} then @{refresh}.
+@function echoch
+@int ch
+@treturn bool `true`, if successful
+@see wechochar(3x)
+*/
+static int
+Wechoch(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	chtype ch = checkch(L, 2);
+	return pushokresult(wechochar(w, ch));
+}
+
+
+/***
+Copy a @{curses.chstr} starting at the current cursor position.
+@function addchstr
+@int chstr cs
+@int[opt] n
+@treturn bool `true`, if successful
+@see waddchnstr(3x)
+*/
+static int
+Waddchstr(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int n = optint(L, 3, -1);
+	chstr *cs = checkchstr(L, 2);
+
+	if (n < 0 || n > (int) cs->len)
+		n = cs->len;
+
+	return pushokresult(waddchnstr(w, cs->str, n));
+}
+
+
+/***
+Call @{move} then @{addchstr}.
+@function mvaddchstr
+@int y
+@int x
+@int[opt] n
+@treturn bool `true`, if successful
+@see mvwaddchnstr(3x)
+*/
+static int
+Wmvaddchstr(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int y = checkint(L, 2);
+	int x = checkint(L, 3);
+	int n = optint(L, 5, -1);
+	chstr *cs = checkchstr(L, 4);
+
+	if (n < 0 || n > (int) cs->len)
+		n = cs->len;
+
+	return pushokresult(mvwaddchnstr(w, y, x, cs->str, n));
+}
+
+
+/***
+Copy a Lua string starting at the current cursor position.
+@function addstr
+@string str
+@int[opt] n
+@treturn bool `true`, if successful
+@see waddnstr(3x)
+*/
+static int
+Waddstr(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	const char *str = luaL_checkstring(L, 2);
+	int n = optint(L, 3, -1);
+	return pushokresult(waddnstr(w, str, n));
+}
+
+
+/***
+Call @{move} then @{addstr}.
+@function mvaddstr
+@int y
+@int x
+@string str
+@int[opt] n
+@treturn bool `true`, if successful
+@see mvwaddnstr(3x)
+*/
+static int
+Wmvaddstr(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int y = checkint(L, 2);
+	int x = checkint(L, 3);
+	const char *str = luaL_checkstring(L, 4);
+	int n = optint(L, 5, -1);
+	return pushokresult(mvwaddnstr(w, y, x, str, n));
+}
+
+
+/***
+Set the background attributes for subsequently written characters.
+@function wbkgdset
+@int ch
+@see wbkgdset(3x)
+*/
+static int
+Wwbkgdset(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	chtype ch = checkch(L, 2);
+	wbkgdset(w, ch);
+	return 0;
+}
+
+
+/***
+Call @{wbkgdset} and then set the background of every position accordingly.
+@function wbkgd
+@int ch
+@treturn bool `true`, if successful
+@see wbkgd(3x)
+*/
+static int
+Wwbkgd(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	chtype ch = checkch(L, 2);
+	return pushokresult(wbkgd(w, ch));
+}
+
+
+/***
+Fetch the current background attribute for the window.
+@function getbkgd
+@treturn int current window background attribute
+@see getbkgd(3x)
+@see wbkgd
+*/
+static int
+Wgetbkgd(lua_State *L)
+{
+	return pushintresult(getbkgd(checkwin(L, 1)));
+}
+
+
+/***
+Set the flush on interrupt behaviour for the window.
+@function intrflush
+@bool bf
+@treturn bool `true`, if successful
+@see intrflush(3x)
+*/
+static int
+Wintrflush(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int bf = lua_toboolean(L, 2);
+	return pushokresult(intrflush(w, bf));
+}
+
+
+/***
+Set the single value keypad keys behaviour for the window.
+@function keypad
+@bool[opt] on
+@treturn bool `true`, if successful
+@see keypad(3x)
+*/
+static int
+Wkeypad(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int bf = lua_isnoneornil(L, 2) ? 1 : lua_toboolean(L, 2);
+	return pushokresult(keypad(w, bf));
+}
+
+
+/***
+Force 8-bit (or 7-bit) input characters for the window.
+@function meta
+@bool on `true` to force 8-bit input characters
+@treturn bool `true`, if successful
+@see meta(3x)
+*/
+static int
+Wmeta(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int bf = lua_toboolean(L, 2);
+	return pushokresult(meta(w, bf));
+}
+
+
+/***
+Force @{getch} to be non-blocking.
+@function nodelay
+@bool on
+@treturn bool `true`, if successful
+@see nodelay(3x)
+*/
+static int
+Wnodelay(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int bf = lua_toboolean(L, 2);
+	return pushokresult(nodelay(w, bf));
+}
+
+
+/***
+For differentiating user input from terminal control sequences.
+@function timeout
+@int delay milliseconds, `0` for blocking, `-1` for non-blocking
+@see wtimeout(3x)
+*/
+static int
+Wtimeout(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int delay = checkint(L, 2);
+	wtimeout(w, delay);
+	return 0;
+}
+
+
+/***
+Return input immediately from this window.
+@function notimeout
+@bool bf
+@treturn bool `true`, if successful
+@fixme ncurses only?
+*/
+static int
+Wnotimeout(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int bf = lua_toboolean(L, 2);
+	return pushokresult(notimeout(w, bf));
+}
+
+
+/***
+The next call to @{refresh} will clear and completely redraw the window.
+@function clearok
+@bool bf
+@treturn bool `true`, if successful
+@see clearok(3x)
+*/
+static int
+Wclearok(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int bf = lua_toboolean(L, 2);
+	return pushokresult(clearok(w, bf));
+}
+
+
+/***
+Use hardware character insert and delete on supporting terminals.
+@function idcok
+@bool bf
+@treturn bool `true`, if successful
+@see idcok(3x)
+*/
+static int
+Widcok(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int bf = lua_toboolean(L, 2);
+	idcok(w, bf);
+	return 0;
+}
+
+
+/***
+Use hardware line insert and delete on supporting terminals.
+@function idlok
+@bool bf
+@treturn bool `true`, if successful
+@see idlok(3x)
+*/
+static int
+Widlok(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int bf = lua_toboolean(L, 2);
+	return pushokresult(idlok(w, bf));
+}
+
+
+/***
+No need to force synchronisation of hardware cursor.
+@function leaveok
+@bool bf
+@treturn bool `true`, if successful
+@see leaveok(3x)
+*/
+static int
+Wleaveok(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int bf = lua_toboolean(L, 2);
+	return pushokresult(leaveok(w, bf));
+}
+
+
+/***
+Scroll up one line when the cursor writes to the last screen position.
+@function scrollok
+@bool bf
+@treturn bool `true`, if successful
+@see scrollok(3x)
+*/
+static int
+Wscrollok(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int bf = lua_toboolean(L, 2);
+	return pushokresult(scrollok(w, bf));
+}
+
+
+/***
+Automatically call @{refresh} whenever the window content is changed.
+@function immedok
+@bool bf
+@treturn bool `true`, if successful
+@see immedok(3x)
+*/
+static int
+Wimmedok(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+#if LCURSES_POSIX_COMPLIANT
+	int bf = lua_toboolean(L, 2);
+	immedok(w, bf);
+	return 0;
+#else
+	return binding_notimplemented(L, "immedok", "curses");
+#endif
+}
+
+
+/***
+Set a software scrolling region for the window.
+@function wsetscrreg
+@int top top line of the scrolling region (line 0 is the first line)
+@int bot bottom line of the scrolling region
+@treturn bool `true`, if successful
+@see wsetscrreg(3x)
+*/
+static int
+Wwsetscrreg(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int top = checkint(L, 2);
+	int bot = checkint(L, 3);
+	return pushokresult(wsetscrreg(w, top, bot));
+}
+
+
+/***
+Overlay this window on top of another non-destructively.
+@function overlay
+@tparam window dst destination window
+@treturn bool `true`, if successful
+@see overlay(3x)
+@see overwrite
+*/
+static int
+Woverlay(lua_State *L)
+{
+	WINDOW *srcwin = checkwin(L, 1);
+	WINDOW *dstwin = checkwin(L, 2);
+	return pushokresult(overlay(srcwin, dstwin));
+}
+
+
+/***
+Destructively overwrite another window with this one.
+@function overwrite
+@tparam window dst destination window
+@treturn bool `true`, if successful
+@see overwrite(3x)
+*/
+static int
+Woverwrite(lua_State *L)
+{
+	WINDOW *srcwin = checkwin(L, 1);
+	WINDOW *dstwin = checkwin(L, 2);
+	return pushokresult(overwrite(srcwin, dstwin));
+}
+
+
+/***
+Overlay a rectangle of this window over another.
+@function copywin
+@tparam window dst destination window
+@int st top row from this window
+@int sl left column from this window
+@int dt top row of rectangle
+@int dl left column of rectangle
+@int db bottom row of rectangle
+@int dr right column of rectangle
+@bool overlay if `true`, copy destructively like @{overlay}
+@treturn bool `true`, if successful
+@see copywin(3x)
+*/
+static int
+Wcopywin(lua_State *L)
+{
+	WINDOW *srcwin = checkwin(L, 1);
+	WINDOW *dstwin = checkwin(L, 2);
+	int sminrow = checkint(L, 3);
+	int smincol = checkint(L, 4);
+	int dminrow = checkint(L, 5);
+	int dmincol = checkint(L, 6);
+	int dmaxrow = checkint(L, 7);
+	int dmaxcol = checkint(L, 8);
+	int woverlay = lua_toboolean(L, 9);
+	return pushokresult(copywin(srcwin, dstwin, sminrow,
+		smincol, dminrow, dmincol, dmaxrow, dmaxcol, woverlay));
+}
+
+
+/***
+Delete the character under the cursor.
+@function delch
+@treturn bool `true`, if successful
+@see wdelch(3x)
+*/
+static int
+Wdelch(lua_State *L)
+{
+	return pushokresult(wdelch(checkwin(L, 1)));
+}
+
+
+/***
+Call @{move} then @{delch}.
+@function mvdelch
+@int y
+@int x
+@treturn bool `true`, if successful
+@see mvwdelch(3x)
+*/
+static int
+Wmvdelch(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int y = checkint(L, 2);
+	int x = checkint(L, 3);
+	return pushokresult(mvwdelch(w, y, x));
+}
+
+
+/***
+Move the lines below the cursor up, to delete the current line.
+@function deleteln
+@treturn bool `true`, if successful
+@see wdeleteln(3x)
+*/
+static int
+Wdeleteln(lua_State *L)
+{
+	return pushokresult(wdeleteln(checkwin(L, 1)));
+}
+
+
+/***
+Move the current line and those below down one line, leaving a new blank line.
+@function insertln
+@treturn bool `true`, if successful
+@see wdeleteln(3x)
+*/
+static int
+Winsertln(lua_State *L)
+{
+	return pushokresult(winsertln(checkwin(L, 1)));
+}
+
+
+/***
+Call @{deleteln} *n* times.
+@function winsdelln
+@int n
+@treturn bool `true`, if successful
+@see winsdelln(3x)
+*/
+static int
+Wwinsdelln(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int n = checkint(L, 2);
+	return pushokresult(winsdelln(w, n));
+}
+
+
+/***
+Read a character from the window input.
+@function getch
+@treturn int character read from input buffer
+@see wgetch(3x)
+@see curses.cbreak
+@see curses.echo
+@see keypad
+*/
+static int
+Wgetch(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int c = wgetch(w);
+
+	if (c == ERR)
+		return 0;
+
+	return pushintresult(c);
+}
+
+
+/***
+Call @{move} then @{getch}
+@function mvgetch
+@int y
+@int x
+@treturn int character read from input buffer
+@see mvwgetch(3x)
+@see getch
+*/
+static int
+Wmvgetch(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int y = checkint(L, 2);
+	int x = checkint(L, 3);
+	int c;
+
+	if (wmove(w, y, x) == ERR)
+		return 0;
+
+	c = wgetch(w);
+
+	if (c == ERR)
+		return 0;
+
+	return pushintresult(c);
+}
+
+
+/***
+Read characters up to the next newline from the window input.
+@function getstr
+@int[opt] n
+@treturn string string read from input buffer
+@see wgetnstr(3x)
+*/
+static int
+Wgetstr(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int n = optint(L, 2, 0);
+	char buf[LUAL_BUFFERSIZE];
+
+	if (n == 0 || n >= LUAL_BUFFERSIZE)
+		n = LUAL_BUFFERSIZE - 1;
+	if (wgetnstr(w, buf, n) == ERR)
+		return 0;
+
+	return pushstringresult(buf);
+}
+
+
+/***
+Call @{move} then @{getstr}.
+@function mvgetstr
+@int y
+@int x
+@int[opt=-1] n
+@treturn string string read from input buffer
+@see mvwgetnstr(3x)
+*/
+static int
+Wmvgetstr(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int y = checkint(L, 2);
+	int x = checkint(L, 3);
+	int n = optint(L, 4, -1);
+	char buf[LUAL_BUFFERSIZE];
+
+	if (n == 0 || n >= LUAL_BUFFERSIZE)
+		n = LUAL_BUFFERSIZE - 1;
+	if (mvwgetnstr(w, y, x, buf, n) == ERR)
+		return 0;
+
+	return pushstringresult(buf);
+}
+
+
+/***
+Fetch the attributed character at the current cursor position.
+@function winch
+@treturn int attributed character read from input buffer
+@see winch(3x)
+*/
+static int
+Wwinch(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	return pushintresult(winch(w));
+}
+
+
+/***
+Call @{move} then @{winch}
+@function mvwinch
+@int y
+@int x
+@treturn int attributed character read from input buffer
+@see mvwinch(3x)
+*/
+static int
+Wmvwinch(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int y = checkint(L, 2);
+	int x = checkint(L, 3);
+	return pushintresult(mvwinch(w, y, x));
+}
+
+
+/***
+Fetch attributed characters from cursor position up to rightmost window position.
+@function winchnstr
+@int n
+@treturn curses.chstr characters from cursor to end of line
+@see winchnstr(3x)
+@see winnstr
+*/
+static int
+Wwinchnstr(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int n = checkint(L, 2);
+	chstr *cs = chstr_new(L, n);
+
+	if (winchnstr(w, cs->str, n) == ERR)
+		return 0;
+
+	return 1;
+}
+
+
+/***
+Call @{move} then @{winchnstr}.
+@function mvwinchnstr
+@int y
+@int x
+@int n
+@treturn curses.chstr characters from cursor to end of line
+@see mvwinchnstr(3x)
+*/
+static int
+Wmvwinchnstr(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int y = checkint(L, 2);
+	int x = checkint(L, 3);
+	int n = checkint(L, 4);
+	chstr *cs = chstr_new(L, n);
+
+	if (mvwinchnstr(w, y, x, cs->str, n) == ERR)
+		return 0;
+
+	return 1;
+}
+
+
+/***
+Fetch a string from cursor position up to rightmost window position.
+@function winnstr
+@int n
+@treturn string string read from input buffer
+@see winnstr(3x)
+@see winchnstr
+*/
+static int
+Wwinnstr(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int n = checkint(L, 2);
+	char buf[LUAL_BUFFERSIZE];
+	char *ptr = buf;
+
+	if (abs(n) >= LUAL_BUFFERSIZE)
+	    ptr = lua_newuserdata(L, (size_t) abs(n) + 1);
+
+	n = winnstr(w, ptr, n);
+	if (n == ERR)
+	    return 0;
+
+	lua_pushlstring(L, ptr, n);
+	return 1;
+}
+
+
+/***
+Call @{move} then @{winnstr}.
+@function mvwinnstr
+@int y
+@int x
+@int n
+@treturn string string read from input buffer
+@see mvwinnstr(3x)
+*/
+static int
+Wmvwinnstr(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int y = checkint(L, 2);
+	int x = checkint(L, 3);
+	int n = checkint(L, 4);
+	char buf[LUAL_BUFFERSIZE];
+	char *ptr = buf;
+
+	if (abs(n) >= LUAL_BUFFERSIZE)
+	    ptr = lua_newuserdata(L, (size_t) abs(n) + 1);
+
+	n = mvwinnstr(w, y, x, ptr, n);
+	if (n == ERR)
+	    return 0;
+
+	lua_pushlstring(L, ptr, n);
+	return 1;
+}
+
+
+/***
+Insert an attributed character before the current cursor position.
+@function winsch
+@int ch
+@treturn bool `true`, if successful
+@see winsch(3x)
+*/
+static int
+Wwinsch(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	chtype ch = checkch(L, 2);
+	return pushokresult(winsch(w, ch));
+}
+
+
+/***
+Call @{move} then @{winsch}.
+@function mvwinsch
+@int y
+@int x
+@int ch
+@treturn bool `true`, if successful
+@see mvwinsch(3x)
+*/
+static int
+Wmvwinsch(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int y = checkint(L, 2);
+	int x = checkint(L, 3);
+	chtype ch = checkch(L, 4);
+	return pushokresult(mvwinsch(w, y, x, ch));
+}
+
+
+/***
+Insert a string of characters before the current cursor position.
+@function winsstr
+@string str
+@treturn bool `true`, if successful
+@see winsstr(3x)
+*/
+static int
+Wwinsstr(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	const char *str = luaL_checkstring(L, 2);
+	return pushokresult(winsnstr(w, str, lua_strlen(L, 2)));
+}
+
+
+/***
+Call @{move} then @{winsstr}.
+@function mvwinsstr
+@int y
+@int x
+@string str
+@treturn bool `true`, if successful
+@see mvwinsstr(3x)
+*/
+static int
+Wmvwinsstr(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int y = checkint(L, 2);
+	int x = checkint(L, 3);
+	const char *str = luaL_checkstring(L, 4);
+	return pushokresult(mvwinsnstr(w, y, x, str, lua_strlen(L, 2)));
+}
+
+
+/***
+Like @{winsstr}, but no more than *n* characters.
+@function winsnstr
+@string str
+@int n
+@treturn bool `true`, if successful
+@see winsnstr(3x)
+*/
+static int
+Wwinsnstr(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	const char *str = luaL_checkstring(L, 2);
+	int n = checkint(L, 3);
+	return pushokresult(winsnstr(w, str, n));
+}
+
+
+/***
+Call @{move} then @{winsnstr}.
+@function mvwinsnstr
+@int y
+@int x
+@string str
+@int n
+@treturn bool `true`, if successful
+@see mvwinsnstr(3x)
+*/
+static int
+Wmvwinsnstr(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int y = checkint(L, 2);
+	int x = checkint(L, 3);
+	const char *str = luaL_checkstring(L, 4);
+	int n = checkint(L, 5);
+	return pushokresult(mvwinsnstr(w, y, x, str, n));
+}
+
+
+/***
+Return a new subpad window object.
+@function subpad
+@int nlines
+@int ncols
+@int begin_y
+@int begin_x
+@treturn window a new subpad window object
+@see subpad(3x)
+*/
+static int
+Wsubpad(lua_State *L)
+{
+	WINDOW *orig = checkwin(L, 1);
+	int nlines  = checkint(L, 2);
+	int ncols   = checkint(L, 3);
+	int begin_y = checkint(L, 4);
+	int begin_x = checkint(L, 5);
+
+	lc_newwin(L, subpad(orig, nlines, ncols, begin_y, begin_x));
+	return 1;
+}
+
+
+/***
+Equivalent to @{refresh} for use with pad windows.
+@function prefresh
+@int st top row from this pad window
+@int sl left column from this pad window
+@int dt top row of rectangle
+@int dl left column of rectangle
+@int db bottom row of rectangle
+@int dr right column of rectangle
+@treturn bool `true`, if successful
+@see prefresh(3x)
+*/
+static int
+Wprefresh(lua_State *L)
+{
+	WINDOW *p = checkwin(L, 1);
+	int pminrow = checkint(L, 2);
+	int pmincol = checkint(L, 3);
+	int sminrow = checkint(L, 4);
+	int smincol = checkint(L, 5);
+	int smaxrow = checkint(L, 6);
+	int smaxcol = checkint(L, 7);
+
+	return pushokresult(prefresh(p, pminrow, pmincol,
+		sminrow, smincol, smaxrow, smaxcol));
+}
+
+
+/***
+Equivalent to @{noutrefresh} for use with pad windows.
+@function pnoutrefresh
+@int st top row from this pad window
+@int sl left column from this pad window
+@int dt top row of rectangle
+@int dl left column of rectangle
+@int db bottom row of rectangle
+@int dr right column of rectangle
+@treturn bool `true`, if successful
+@see pnoutrefresh(3x)
+*/
+static int
+Wpnoutrefresh(lua_State *L)
+{
+	WINDOW *p = checkwin(L, 1);
+	int pminrow = checkint(L, 2);
+	int pmincol = checkint(L, 3);
+	int sminrow = checkint(L, 4);
+	int smincol = checkint(L, 5);
+	int smaxrow = checkint(L, 6);
+	int smaxcol = checkint(L, 7);
+
+	return pushokresult(pnoutrefresh(p, pminrow, pmincol,
+		sminrow, smincol, smaxrow, smaxcol));
+}
+
+
+/***
+An efficient equivalent to @{addch} followed by @{refresh}.
+@function pechochar
+@int ch
+@treturn bool `true`, if successful
+@see pechochar(3x)
+*/
+static int
+Wpechochar(lua_State *L)
+{
+	WINDOW *p = checkwin(L, 1);
+	chtype ch = checkch(L, 2);
+	return pushokresult(pechochar(p, ch));
+}
+
+
+/***
+Turn off the given attributes for subsequent writes to the window.
+@function attroff
+@int attrs
+@treturn bool `true`, if successful
+@see wattroff(3x)
+@see standend
+*/
+static int
+Wattroff(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int attrs = checkint(L, 2);
+	return pushokresult(wattroff(w, attrs));
+}
+
+
+/***
+Turn on the given attributes for subsequent writes to the window.
+@function attron
+@int attrs
+@treturn bool `true`, if successful
+@see wattron(3x)
+*/
+static int
+Wattron(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int attrs = checkint(L, 2);
+	return pushokresult(wattron(w, attrs));
+}
+
+
+/***
+Set the given attributes for subsequent writes to the window.
+@function attrset
+@int attrs
+@treturn bool `true`, if successful
+@see wattrset(3x)
+*/
+static int
+Wattrset(lua_State *L)
+{
+	WINDOW *w = checkwin(L, 1);
+	int attrs = checkint(L, 2);
+	return pushokresult(wattrset(w, attrs));
+}
+
+
+/***
+Turn off all attributes for subsequent writes to the window.
+@function standend
+@treturn bool `true`, if successful
+@see wstandend(3x)
+*/
+static int
+Wstandend(lua_State *L)
+{
+	return pushokresult(wstandend(checkwin(L, 1)));
+}
+
+
+/***
+Set `A_STANDOUT` for subsequent writes to the window.
+@function standout
+@treturn bool `true`, if successful
+@see wstandout(3x)
+*/
+static int
+Wstandout(lua_State *L)
+{
+	return pushokresult(wstandout(checkwin(L, 1)));
+}
+
+
+static const luaL_Reg curses_window_fns[] =
+{
+	LCURSES_FUNC( W__tostring	),
+	LCURSES_FUNC( Waddch		),
+	LCURSES_FUNC( Waddchstr		),
+	LCURSES_FUNC( Waddstr		),
+	LCURSES_FUNC( Wattroff		),
+	LCURSES_FUNC( Wattron		),
+	LCURSES_FUNC( Wattrset		),
+	LCURSES_FUNC( Wborder		),
+	LCURSES_FUNC( Wbox		),
+	LCURSES_FUNC( Wclear		),
+	LCURSES_FUNC( Wclearok		),
+	LCURSES_FUNC( Wclone		),
+	LCURSES_FUNC( Wclose		),
+	LCURSES_FUNC( Wclrtobot		),
+	LCURSES_FUNC( Wclrtoeol		),
+	LCURSES_FUNC( Wcopywin		),
+	LCURSES_FUNC( Wcursyncup	),
+	LCURSES_FUNC( Wdelch		),
+	LCURSES_FUNC( Wdeleteln		),
+	LCURSES_FUNC( Wderive		),
+	LCURSES_FUNC( Wechoch		),
+	LCURSES_FUNC( Werase		),
+	LCURSES_FUNC( Wgetbegyx		),
+	LCURSES_FUNC( Wgetbkgd		),
+	LCURSES_FUNC( Wgetch		),
+	LCURSES_FUNC( Wgetmaxyx		),
+	LCURSES_FUNC( Wgetparyx		),
+	LCURSES_FUNC( Wgetstr		),
+	LCURSES_FUNC( Wgetyx		),
+	LCURSES_FUNC( Whline		),
+	LCURSES_FUNC( Widcok		),
+	LCURSES_FUNC( Widlok		),
+	LCURSES_FUNC( Wimmedok		),
+	LCURSES_FUNC( Winsertln		),
+	LCURSES_FUNC( Wintrflush	),
+	LCURSES_FUNC( Wis_linetouched	),
+	LCURSES_FUNC( Wis_wintouched	),
+	LCURSES_FUNC( Wkeypad		),
+	LCURSES_FUNC( Wleaveok		),
+	LCURSES_FUNC( Wmeta		),
+	LCURSES_FUNC( Wmove		),
+	LCURSES_FUNC( Wmove_derived	),
+	LCURSES_FUNC( Wmove_window	),
+	LCURSES_FUNC( Wmvaddch		),
+	LCURSES_FUNC( Wmvaddchstr	),
+	LCURSES_FUNC( Wmvaddstr		),
+	LCURSES_FUNC( Wmvdelch		),
+	LCURSES_FUNC( Wmvgetch		),
+	LCURSES_FUNC( Wmvgetstr		),
+	LCURSES_FUNC( Wmvhline		),
+	LCURSES_FUNC( Wmvvline		),
+	LCURSES_FUNC( Wmvwinch		),
+	LCURSES_FUNC( Wmvwinchnstr	),
+	LCURSES_FUNC( Wmvwinnstr	),
+	LCURSES_FUNC( Wmvwinsch		),
+	LCURSES_FUNC( Wmvwinsnstr	),
+	LCURSES_FUNC( Wmvwinsstr	),
+	LCURSES_FUNC( Wnodelay		),
+	LCURSES_FUNC( Wnotimeout	),
+	LCURSES_FUNC( Wnoutrefresh	),
+	LCURSES_FUNC( Woverlay		),
+	LCURSES_FUNC( Woverwrite	),
+	LCURSES_FUNC( Wpechochar	),
+	LCURSES_FUNC( Wpnoutrefresh	),
+	LCURSES_FUNC( Wprefresh		),
+	LCURSES_FUNC( Wredrawln		),
+	LCURSES_FUNC( Wredrawwin	),
+	LCURSES_FUNC( Wrefresh		),
+	LCURSES_FUNC( Wresize		),
+	LCURSES_FUNC( Wscrl		),
+	LCURSES_FUNC( Wscrollok		),
+	LCURSES_FUNC( Wstandend		),
+	LCURSES_FUNC( Wstandout		),
+	LCURSES_FUNC( Wsub		),
+	LCURSES_FUNC( Wsubpad		),
+	LCURSES_FUNC( Wsyncdown		),
+	LCURSES_FUNC( Wsyncok		),
+	LCURSES_FUNC( Wsyncup		),
+	LCURSES_FUNC( Wtimeout		),
+	LCURSES_FUNC( Wtouch		),
+	LCURSES_FUNC( Wtouchline	),
+	LCURSES_FUNC( Wvline		),
+	LCURSES_FUNC( Wwbkgd		),
+	LCURSES_FUNC( Wwbkgdset		),
+	LCURSES_FUNC( Wwinch		),
+	LCURSES_FUNC( Wwinchnstr	),
+	LCURSES_FUNC( Wwinnstr		),
+	LCURSES_FUNC( Wwinsch		),
+	LCURSES_FUNC( Wwinsdelln	),
+	LCURSES_FUNC( Wwinsnstr		),
+	LCURSES_FUNC( Wwinsstr		),
+	LCURSES_FUNC( Wwsetscrreg	),
+	{"__gc",     Wclose		}, /* rough safety net */
+	{NULL, NULL}
+};
+
+
+LUALIB_API int
+luaopen_curses_window(lua_State *L)
+{
+	int t, mt;
+
+	luaL_register(L, "curses.window", curses_window_fns);
+	t = lua_gettop(L);
+
+	luaL_newmetatable(L, WINDOWMETA);
+	mt = lua_gettop(L);
+
+	lua_pushvalue(L, mt);
+	lua_setfield(L, mt, "__index");		/* mt.__index = mt */
+	lua_pushliteral(L, "CursesWindow");
+	lua_setfield(L, mt, "_type");		/* mt._type = "Curses Window" */
+
+	/* for k,v in pairs(t) do mt[k]=v end */
+	for (lua_pushnil(L); lua_next(L, t) != 0;)
+		lua_setfield(L, mt, lua_tostring(L, -2));
+
+	lua_pop(L, 1);				/* pop mt */
+
+	return 1;
+}