about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--src/Makefile6
-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
-rw-r--r--src/linit.c2
-rw-r--r--src/lua.c1
-rw-r--r--src/lualib.h4
12 files changed, 4991 insertions, 4 deletions
diff --git a/src/Makefile b/src/Makefile
index 1574be6..2b970bd 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -28,7 +28,7 @@ CORE_O=	lapi.o lcode.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o \
 	lundump.o lvm.o lzio.o \
 	kilo.o
 LIB_O=	lauxlib.o lbaselib.o lcurseslib.o ldblib.o liolib.o lmathlib.o \
-	loslib.o ltablib.o lstrlib.o loadlib.o linit.o
+	loslib.o ltablib.o lstrlib.o loadlib.o linit.o lcurses/curses.o
 
 LUA_T=	teliva
 LUA_O=	lua.o
@@ -180,4 +180,8 @@ lzio.o: lzio.c lua.h luaconf.h llimits.h lmem.h lstate.h lobject.h ltm.h \
 print.o: print.c ldebug.h lstate.h lua.h luaconf.h lobject.h llimits.h \
   ltm.h lzio.h lmem.h lopcodes.h lundump.h
 
+lcurses/curses.o: lcurses/curses.c lcurses/window.c lcurses/chstr.c \
+  lcurses/_helpers.c lcurses/compat-5.2.c lcurses/compat-5.2.h \
+  lcurses/strlcpy.c
+
 # (end of Makefile)
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;
+}
diff --git a/src/linit.c b/src/linit.c
index 6211f39..48eb082 100644
--- a/src/linit.c
+++ b/src/linit.c
@@ -22,7 +22,7 @@ static const luaL_Reg lualibs[] = {
   {LUA_OSLIBNAME, luaopen_os},
   {LUA_STRLIBNAME, luaopen_string},
   {LUA_MATHLIBNAME, luaopen_math},
-  {LUA_CURSESLIBNAME, luaopen_curses},
+  {LUA_CURSESLIBNAME, luaopen_curses_c},
   {LUA_DBLIBNAME, luaopen_debug},
   {NULL, NULL}
 };
diff --git a/src/lua.c b/src/lua.c
index bfb8809..4e09b25 100644
--- a/src/lua.c
+++ b/src/lua.c
@@ -650,6 +650,7 @@ static int pmain (lua_State *L) {
   if (argv[0] && argv[0][0]) progname = argv[0];
   lua_gc(L, LUA_GCSTOP, 0);  /* stop collector during initialization */
   luaL_openlibs(L);  /* open libraries */
+  dofile(L, "lcurses/curses.lua");
   lua_gc(L, LUA_GCRESTART, 0);
   s->status = handle_luainit(L);
   if (s->status != 0) return 0;
diff --git a/src/lualib.h b/src/lualib.h
index 02924dd..e861154 100644
--- a/src/lualib.h
+++ b/src/lualib.h
@@ -33,8 +33,8 @@ LUALIB_API int (luaopen_string) (lua_State *L);
 #define LUA_MATHLIBNAME	"math"
 LUALIB_API int (luaopen_math) (lua_State *L);
 
-#define LUA_CURSESLIBNAME	"curses"
-LUALIB_API int (luaopen_curses) (lua_State *L);
+#define LUA_CURSESLIBNAME	"curses_c"
+LUALIB_API int (luaopen_curses_c) (lua_State *L);
 
 #define LUA_DBLIBNAME	"debug"
 LUALIB_API int (luaopen_debug) (lua_State *L);