diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile | 6 | ||||
-rw-r--r-- | src/lcurses/_helpers.c | 210 | ||||
-rw-r--r-- | src/lcurses/chstr.c | 293 | ||||
-rw-r--r-- | src/lcurses/compat-5.2.c | 682 | ||||
-rw-r--r-- | src/lcurses/compat-5.2.h | 168 | ||||
-rw-r--r-- | src/lcurses/curses.c | 1555 | ||||
-rw-r--r-- | src/lcurses/curses.lua | 58 | ||||
-rw-r--r-- | src/lcurses/strlcpy.c | 76 | ||||
-rw-r--r-- | src/lcurses/window.c | 1940 | ||||
-rw-r--r-- | src/linit.c | 2 | ||||
-rw-r--r-- | src/lua.c | 1 | ||||
-rw-r--r-- | src/lualib.h | 4 |
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); |