simple library for fancy terminal io based on termbox
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

1797 lines
40 KiB

/* termfx.c
*
* provide simple terminal interface for lua
*
* Gunnar Zötl <gz@tset.de>, 2014-2015
* Released under the terms of the MIT license. See file LICENSE for details.
*/
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h>
#include "lua.h"
#include "lauxlib.h"
#include "mini_utf8.h"
#include "termbox.h"
#include "tbutils.h"
#include "termfx.h"
/* userdata for a termbox cell */
typedef struct tb_cell TfxCell;
/* userdata for a termfx offscreen buffer */
typedef struct {
int w, h;
uint16_t fg, bg;
struct tb_cell *buf;
} TfxBuffer;
static const int top_left_coord = 1;
static uint16_t default_fg, default_bg;
static int initialized = 0;
static uint64_t mstimer_tm0 = 0;
/* helper: try to get a char from the stack at index. If the value there
* is a number, return it. If the value is a string of length 1, return
* the value of the first char. If it is a string of length >1, try to
* read it as an utf8 encoded char.
*/
static uint32_t _tfx_getchar(lua_State *L, int index)
{
uint32_t res = 0;
int t = lua_type(L, index);
if (t == LUA_TNUMBER)
res = (uint32_t) lua_tointeger(L, index);
else if (t == LUA_TSTRING) {
const char* str = lua_tostring(L, index);
int l = strlen(str);
if (l == 1)
res = *str;
else if (l > 1) {
res = mini_utf8_decode(&str);
if (*str != 0)
return luaL_argerror(L, index, "invalid char");
}
} else {
return luaL_argerror(L, index, "number or string");
}
return res;
}
/* helper: as above, but if there's nil or nothing on the stack at the
* index, return the default value.
*/
static uint32_t _tfx_optchar(lua_State *L, int index, uint32_t dfl)
{
if (!lua_isnoneornil(L, index))
return _tfx_getchar(L, index);
return dfl;
}
/* helper: check whether the value of the given index on the lua stack
* is a userdata of the given type. Return 1 for yes, 0 for no.
*/
static int _tfx_isUserdataOfType(lua_State *L, int index, const char* type)
{
if (lua_isuserdata(L, index)) {
if (lua_getmetatable(L, index)) {
luaL_getmetatable(L, type);
if (lua_rawequal(L, -1, -2)) {
lua_pop(L, 2);
return 1;
}
lua_pop(L, 2);
}
}
return 0;
}
/* helper: return a millisecond timer, which is 0 at the load time of
* the library, so that the result will fit into an uint32_t for some time
* (a little more than 49 days)
*/
static uint32_t _tfx_getMsTimer(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
uint64_t tm = tv.tv_sec * 1000 + tv.tv_usec / 1000 - mstimer_tm0;
if (tm > INT_MAX) {
mstimer_tm0 += INT_MAX;
tm -= INT_MAX;
}
return (uint32_t) tm;
}
/*** TfxCell Userdata handling ***/
/* tfx_isCell
*
* If the value at the given acceptable index is a full userdata of the
* type TFXCELL, then return 1, else return 0.
*
* Arguments:
* L Lua State
* index stack index where the userdata is expected
*/
static int tfx_isCell(lua_State *L, int index)
{
return _tfx_isUserdataOfType(L, index, TFXCELL);
}
/* tfx_toCell
*
* If the value at the given acceptable index is a full userdata, returns
* its block address. Otherwise, returns NULL.
*
* Arguments:
* L Lua State
* index stack index where the userdata is expected
*/
static TfxCell* tfx_toCell(lua_State *L, int index)
{
TfxCell *tfxcell = (TfxCell*) lua_touserdata(L, index);
return tfxcell;
}
/* tfx_checkCell
*
* Checks whether the function argument narg is a userdata of the type
* TFXCELL. If so, returns its block address, else throw an error.
*
* Arguments:
* L Lua State
* index stack index where the userdata is expected
*/
static TfxCell* tfx_checkCell(lua_State *L, int index)
{
TfxCell *tfxcell = (TfxCell*) luaL_checkudata(L, index, TFXCELL);
return tfxcell;
}
/* tfx_pushCell
*
* create a new, empty TfxCell userdata and push it to the stack.
*
* Arguments:
* L Lua state
*/
static TfxCell* tfx_pushCell(lua_State *L)
{
TfxCell *tfxcell = (TfxCell*) lua_newuserdata(L, sizeof(TfxCell));
luaL_getmetatable(L, TFXCELL);
lua_setmetatable(L, -2);
return tfxcell;
}
/* tfx__toStringCell
*
* __tostring metamethod for the TfxCell userdata.
* Returns a string representation of the TfxCell
*
* Arguments:
* L Lua State
*
* Lua Stack:
* 1 TfxCell userdata
*
* Lua Returns:
* +1 the string representation of the TfxCell userdata
*/
static int tfx__tostringCell(lua_State *L)
{
TfxCell *tfxcell = (TfxCell*) lua_touserdata(L, 1);
lua_pushfstring(L, "%s (%p)", TFXCELL, tfxcell);
return 1;
}
/* tfx__indexCell
*
* __index metamethod for the TfxCell userdata.
* Returns a the value accessible at the index which is on top of the
* stack, or nil of there is no such value
*
* Arguments:
* L Lua State
*
* Lua Stack:
* 1 TfxCell userdata
* 2 index to access
*
* Lua Returns:
* +1 value accessible through index
*/
static int tfx__indexCell(lua_State *L)
{
TfxCell *tfxcell = tfx_checkCell(L, 1);
const char* what = luaL_checkstring(L, 2);
if (!strcmp(what, "ch"))
lua_pushinteger(L, tfxcell->ch);
else if (!strcmp(what, "fg"))
lua_pushinteger(L, tfxcell->fg);
else if (!strcmp(what, "bg"))
lua_pushinteger(L, tfxcell->bg);
else
lua_pushnil(L);
return 1;
}
/* tfx__newindexCell
*
* __newindex metamethod for the TfxCell userdata.
* Sets a the value accessible at the index which is on the stack.
*
* Arguments:
* L Lua State
*
* Lua Stack:
* 1 TfxCell userdata
* 2 index to set
* 3 value to set
*
* Lua Returns:
* -
*/
static int tfx__newindexCell(lua_State *L)
{
TfxCell *tfxcell = tfx_checkCell(L, 1);
const char* what = luaL_checkstring(L, 2);
uint32_t val = (uint32_t) luaL_checkinteger(L, 3);
if (!strcmp(what, "ch"))
tfxcell->ch = val < ' ' ? ' ' : val;
else if (!strcmp(what, "fg"))
tfxcell->fg = val;
else if (!strcmp(what, "bg"))
tfxcell->bg = val;
return 0;
}
/* tfx_newCell
*
* create a new TfxCell object, initialize it, put it into a userdata and
* return it to the user.
*
* Arguments:
* L Lua State
*
* Lua Stack:
* +1 (opt) char to store, defaults to ' '
* +2 (opt) forgeround color, defaults to tfx default foreground color
* +3 (opt) background color, defaults to tfx default background color
*
* Lua Returns:
* +1 the TfxCell userdata
*/
static int tfx_newCell(lua_State *L)
{
maxargs(L, 3);
uint32_t ch = _tfx_optchar(L, 1, ' ');
uint16_t fg = (uint16_t) luaL_optinteger(L, 2, default_fg) & 0xFFFF;
uint16_t bg = (uint16_t) luaL_optinteger(L, 3, default_bg) & 0xFFFF;
TfxCell *tfxcell = tfx_pushCell(L);
tfxcell->ch = ch < ' ' ? ' ' : ch;
tfxcell->fg = fg;
tfxcell->bg = bg;
return 1;
}
/* metamethods for the TfxCell userdata
*/
static const luaL_Reg tfx_CellMeta[] = {
{"__tostring", tfx__tostringCell},
{"__index", tfx__indexCell},
{"__newindex", tfx__newindexCell},
{NULL, NULL}
};
/*** TfxBuffer Userdata handling ***/
/* tfx_isBuffer
*
* If the value at the given acceptable index is a full userdata of the
* type TFXBUFFER, then return 1, else return 0.
*
* Arguments:
* L Lua State
* index stack index where the userdata is expected
*/
static int tfx_isBuffer(lua_State *L, int index)
{
return _tfx_isUserdataOfType(L, index, TFXBUFFER);
}
/* tfx_checkBuffer
*
* Checks whether the function argument narg is a userdata of the type
* TFXBUFFER. If so, returns its block address, else throw an error.
*
* Arguments:
* L Lua State
* index stack index where the userdata is expected
*/
static TfxBuffer* tfx_checkBuffer(lua_State *L, int index)
{
TfxBuffer *tfxbuf = (TfxBuffer*) luaL_checkudata(L, index, TFXBUFFER);
return tfxbuf;
}
/* tfx_pushBuffer
*
* create a new, empty TfxBuffer userdata and push it to the stack.
*
* Arguments:
* L Lua state
*/
static TfxBuffer* tfx_pushBuffer(lua_State *L)
{
TfxBuffer *tfxbuf = (TfxBuffer*) lua_newuserdata(L, sizeof(TfxBuffer));
luaL_getmetatable(L, TFXBUFFER);
lua_setmetatable(L, -2);
return tfxbuf;
}
/*** Housekeeping metamethods ***/
/* tfx__gcBuffer
*
* __gc metamethod for the TfxBuffer userdata.
*
* Arguments:
* L Lua State
*
* Lua Stack:
* 1 TfxBuffer userdata
*/
static int tfx__gcBuffer(lua_State *L)
{
TfxBuffer *tfxbuf = (TfxBuffer*) lua_touserdata(L, 1);
if (tfxbuf->buf) free(tfxbuf->buf);
return 0;
}
/* tfx__tostringBuffer
*
* __tostring metamethod for the TfxBuffer userdata.
* Returns a string representation of the TfxBuffer
*
* Arguments:
* L Lua State
*
* Lua Stack:
* 1 TfxBuffer userdata
*
* Lua Returns:
* +1 the string representation of the TfxCell userdata
*/
static int tfx__tostringBuffer(lua_State *L)
{
TfxBuffer *tfxbuf = (TfxBuffer*) lua_touserdata(L, 1);
lua_pushfstring(L, "%s (%p)", TFXBUFFER, tfxbuf);
return 1;
}
/* metamethods for the TfxBuffer userdata
*/
static const luaL_Reg tfx_BufferMeta[] = {
{"__gc", tfx__gcBuffer},
{"__tostring", tfx__tostringBuffer},
{NULL, NULL}
};
/* tfx_newBuffer
*
* create a new TfxBuffer object, initialize it, put it into a userdata
* and return it to the user.
*
* Arguments:
* L Lua State
*
* Lua Stack:
* -
*
* Lua Returns:
* +1 the TfxBuffer userdata
*/
static int tfx_newBuffer(lua_State *L)
{
maxargs(L, 2);
int w = (int) luaL_checkinteger(L, 1);
int h = (int) luaL_checkinteger(L, 2);
if (w < 1 || h < 1)
return luaL_error(L, "buffer dimensions must be >=1");
TfxBuffer *tfxbuf = tfx_pushBuffer(L);
tfxbuf->buf = calloc(w * h, sizeof(struct tb_cell));
tfxbuf->w = w;
tfxbuf->h = h;
tfxbuf->fg = default_fg;
tfxbuf->bg = default_bg;
return 1;
}
/* tfx_bufAttributes
*
* sets or gets default foreground and background attributes on a buffer.
*
* Arguments:
* L Lua State
*
* Lua Stack:
* 1 TfxBuffer userdata
* 2 (opt) foreground attributes
* 3 (opt) background attributes
*
* Lua Returns:
* +1 buffer default foreground attribute
* +2 buffer default background attribute
*/
static int tfx_bufAttributes(lua_State *L)
{
maxargs(L, 3);
TfxBuffer *tfxbuf = tfx_checkBuffer(L, 1);
if (lua_gettop(L) > 1) {
tfxbuf->fg = (uint16_t) luaL_optinteger(L, 2, tfxbuf->fg) & 0xFFFF;
tfxbuf->bg = (uint16_t) luaL_optinteger(L, 3, tfxbuf->bg) & 0xFFFF;
}
lua_pushinteger(L, tfxbuf->fg);
lua_pushinteger(L, tfxbuf->bg);
return 2;
}
/* tfx_bufClear
*
* clears a buffer to the default foreground and background attributes.
* If the optional foreground and background attribute arguments are
* given, these will be set using tfx_bufSetAttributes before clearing.
*
* Arguments:
* L Lua State
*
* Lua Stack:
* 1 TfxBuffer userdata
* 2 (opt) foreground attributes, defaults to buffer default foreground
* 3 (opt) background attributes, defaults to buffer default background
*
* Lua Returns:
* -
*/
static int tfx_bufClear(lua_State *L)
{
if (lua_gettop(L) > 1)
tfx_bufAttributes(L);
TfxBuffer *tfxbuf = tfx_checkBuffer(L, 1);
int c;
for (c = 0; c < tfxbuf->w * tfxbuf->h; ++c) {
TfxCell *cell = &tfxbuf->buf[c];
cell->fg = tfxbuf->fg;
cell->bg = tfxbuf->bg;
cell->ch = ' ';
}
return 0;
}
/* _tfx_bufChangeCell
*
* analog to tb_changecell() for buffers
*/
static int _tfx_bufChangeCell(TfxBuffer *tfxbuf, int x, int y, TfxCell *c)
{
if (x > tfxbuf->w || y > tfxbuf->h || x < 0 || y < 0)
return 0;
TfxCell *cell = &tfxbuf->buf[y * tfxbuf->w + x];
*cell = *c;
return 1;
}
/* tfx_bufSetcell
*
* sets a cell in a buffer. Either uses the default values for foreground
* and background, or the values passed on the lua stack.
*
* Arguments:
* L Lua State
*
* Lua Stack:
* 1 TfxBuffer userdata
* 2 x coordinate of cell
* 3 y coordinate of cell
* either
* 4 TfxCell
* or
* 4 (opt) char, defaults to ' '
* 5 (opt) foreground attributes, defaults to buffer default foreground
* 6 (opt) background attributes, defaults to buffer default background
*
* Lua Returns:
* -
*/
static int tfx_bufSetcell(lua_State *L)
{
TfxBuffer *tfxbuf = tfx_checkBuffer(L, 1);
int x = (int) luaL_checkinteger(L, 2) - top_left_coord;
int y = (int) luaL_checkinteger(L, 3) - top_left_coord;
uint32_t ch = ' ';
uint16_t fg = TB_WHITE, bg = TB_BLACK;
if (tfx_isCell(L, 4)) {
maxargs(L, 4);
TfxCell *tfxcell = tfx_toCell(L, 4);
ch = tfxcell->ch;
fg = tfxcell->fg;
bg = tfxcell->bg;
} else {
maxargs(L, 6);
ch = _tfx_optchar(L, 4, ' ');
fg = (uint16_t) luaL_optinteger(L, 5, tfxbuf->fg) & 0xFFFF;
bg = (uint16_t) luaL_optinteger(L, 6, tfxbuf->bg) & 0xFFFF;
}
TfxCell cell;
cell.ch = ch < '?' ? ' ' : ch;
cell.fg = fg;
cell.bg = bg;
_tfx_bufChangeCell(tfxbuf, x, y, &cell);
return 0;
}
/* tfx_bufGetCell
*
* gets data from a cell in a buffer.
*
* Arguments:
* L Lua State
*
* Lua Stack:
* 1 TfxBuffer userdata
* 2 x coordinate of cell
* 3 y coordinate of cell
*
* Lua Returns:
* a TfxCell with the data from the read cell
*/
int tfx_bufGetCell(lua_State *L)
{
TfxBuffer *tfxbuf = tfx_checkBuffer(L, 1);
int x = (int) luaL_checkinteger(L, 2) - top_left_coord;
int y = (int) luaL_checkinteger(L, 3) - top_left_coord;
if (x < 0 || x >= tfxbuf->w || y < 0 || y >= tfxbuf->h) {
lua_pushnil(L);
return 1;
}
TfxCell *tfxcell = tfx_pushCell(L);
*tfxcell = CELL(tfxbuf->buf, tfxbuf->w, x, y);
return 1;
}
/* tfx_bufBlit
*
* blits the contents of a buffer to another buffer. Just a modified
* copy of my improved tb_blit() routine.
*
* Arguments:
* L Lua State
*
* Lua Stack:
* 1 destination TfxBuffer
* 2 x coordinate of cell
* 3 y coordinate of cell
* 4 source TfxBuffer
*
* Lua Returns:
* -
*/
static int tfx_bufBlit(lua_State *L)
{
TfxBuffer *tfxbuf = tfx_checkBuffer(L, 1);
int x = (int) luaL_checkinteger(L, 2) - top_left_coord;
int y = (int) luaL_checkinteger(L, 3) - top_left_coord;
TfxBuffer *other = tfx_checkBuffer(L, 4);
tbu_blitbuffer(tfxbuf->buf, tfxbuf->w, tfxbuf->h, x, y, other->buf, other->w, other->h);
return 0;
}
/* tfx_bufRect
*
* draws a rectangle. Either uses the default values for foreground and
* background, or the values passed on the lua stack.
*
* Arguments:
* L Lua State
*
* Lua Stack:
* 1 TfxBuffer userdata
* 2 x coordinate of rect
* 3 y coordinate of rect
* 4 width of rect
* 5 height of rect
* either
* 6 TfxCell
* or
* 6 (opt) char, defaults to ' '
* 7 (opt) foreground attributes, defaults to buffer default foreground
* 8 (opt) background attributes, defaults to buffer default background
*
* Lua Returns:
* false if the rectangle was entirely outside of the terminal, true if not
*/
static int tfx_bufRect(lua_State *L)
{
TfxCell cel, *celp = &cel;
TfxBuffer *tfxbuf = tfx_checkBuffer(L, 1);
int x = (int) luaL_checkinteger(L, 2) - top_left_coord;
int y = (int) luaL_checkinteger(L, 3) - top_left_coord;
int w = (int) luaL_checkinteger(L, 4);
int h = (int) luaL_checkinteger(L, 5);
if (tfx_isCell(L, 6)) {
maxargs(L, 6);
celp = tfx_toCell(L, 6);
} else {
maxargs(L, 8);
cel.ch = _tfx_optchar(L, 6, 0);
cel.fg = (uint16_t) luaL_optinteger(L, 7, default_fg) & 0xFFFF;
cel.bg = (uint16_t) luaL_optinteger(L, 8, default_bg) & 0xFFFF;
}
tbu_fillbufferregion(tfxbuf->buf, tfxbuf->w, tfxbuf->h, x, y, w, h, celp);
return 0;
}
/* tfx_bufCopyRegion
*
* copies a rectangular region of a buffer to another place
*
* Arguments:
* L Lua State
*
* Lua Stack
* 1 TfxBuffer userdata
* 2 target x coordinate for copy
* 3 target y coordinate for copy
* 4 source x coordinate
* 5 source y coordinate
* 6 width of rectangle to copy
* 7 height of rectangle to copy
*
* Lua Returns:
* -
*/
static int tfx_bufCopyRegion(lua_State *L)
{
TfxBuffer *tfxbuf = tfx_checkBuffer(L, 1);
int tx = (int) luaL_checkinteger(L, 2) - top_left_coord;
int ty = (int) luaL_checkinteger(L, 3) - top_left_coord;
int x = (int) luaL_checkinteger(L, 4) - top_left_coord;
int y = (int) luaL_checkinteger(L, 5) - top_left_coord;
int w = (int) luaL_checkinteger(L, 6);
int h = (int) luaL_checkinteger(L, 7);
tbu_copybufferregion(tfxbuf->buf, tfxbuf->w, tfxbuf->h, tx, ty, x, y, w, h);
return 0;
}
/* tfx_bufScrollRegion
*
* scrolls a rectangular region of a buffer
*
* Arguments:
* L Lua State
*
* Lua Stack
* 1 TfxBuffer userdata
* 2 x coordinate of rectangle to scroll
* 3 y coordinate of rectangle to scroll
* 4 width of rectangle to scroll
* 5 height of rectangle to scroll
* 6 (opt) x scroll direction (-1, 0, 1), defaults to 0
* 7 (opt) y scroll direction (-1, 0, 1), defaults to 0
* either
* 8 TfxCell
* or
* 8 (opt) char, defaults to ' '
* 9 (opt) foreground attributes, defaults to buffer default foreground
* 10 (opt) background attributes, defaults to buffer default background
*
* Lua Returns:
* -
*
* Note:
* arguments 6 and 7 (scroll directions) are ints, but only their sign is
* used. Scrolling is always by at most 1 cell.
*/
int tfx_bufScrollRegion(lua_State *L)
{
if (!initialized) return 0;
TfxCell cel, *celp = &cel;
TfxBuffer *tfxbuf = tfx_checkBuffer(L, 1);
int x = (int) luaL_checkinteger(L, 2) - top_left_coord;
int y = (int) luaL_checkinteger(L, 3) - top_left_coord;
int w = (int) luaL_checkinteger(L, 4);
int h = (int) luaL_checkinteger(L, 5);
int sx = (int) luaL_optinteger(L, 6, 0);
int sy = (int) luaL_optinteger(L, 7, 0);
if (tfx_isCell(L, 8)) {
maxargs(L, 8);
celp = tfx_toCell(L, 8);
} else {
maxargs(L, 10);
cel.ch = _tfx_optchar(L, 8, 0);
cel.fg = (uint16_t) luaL_optinteger(L, 9, default_fg) & 0xFFFF;
cel.bg = (uint16_t) luaL_optinteger(L, 10, default_bg) & 0xFFFF;
}
tbu_scrollbufferregion(tfxbuf->buf, tfxbuf->w, tfxbuf->h, x, y, w, h, sx, sy, celp);
return 0;
}
/* tfx_bufWidth
*
* returns the width of the buffer
*
* Arguments:
* L Lua State
*
* Lua Stack:
* 1 TfxBuffer userdata
*
* Lua Returns:
* +1 width of buffer
*/
static int tfx_bufWidth(lua_State *L)
{
maxargs(L, 1);
TfxBuffer *tfxbuf = tfx_checkBuffer(L, 1);
lua_pushinteger(L, tfxbuf->w);
return 1;
}
/* tfx_bufHeight
*
* returns the height of the buffer
*
* Arguments:
* L Lua State
*
* Lua Stack:
* 1 TfxBuffer userdata
*
* Lua Returns:
* +1 height of buffer
*/
static int tfx_bufHeight(lua_State *L)
{
maxargs(L, 1);
TfxBuffer *tfxbuf = tfx_checkBuffer(L, 1);
lua_pushinteger(L, tfxbuf->h);
return 1;
}
/* tfx_bufSize
*
* returns width and height of the buffer
*
* Arguments:
* L Lua State
*
* Lua Stack:
* 1 TfxBuffer userdata
*
* Lua Returns:
* +1 width of buffer
* +2 height of buffer
*/
static int tfx_bufSize(lua_State *L)
{
maxargs(L, 1);
TfxBuffer *tfxbuf = tfx_checkBuffer(L, 1);
lua_pushinteger(L, tfxbuf->w);
lua_pushinteger(L, tfxbuf->h);
return 2;
}
/*** termfx stuff ***/
/* tfx_init
*
* initialize TermFX
*
* Arguments:
* L Lua State
*
* Lua Stack:
* 1 (opt) boolean value, if true, then all coordinates will be
* 0-based, otherwise they will be 1-based.
*
* Lua Returns:
* -
*/
static int tfx_init(lua_State *L)
{
maxargs(L, 0);
default_fg = TB_WHITE;
default_bg = TB_BLACK;
int status = tb_init();
if (status < 0) {
switch(status) {
case TB_EUNSUPPORTED_TERMINAL: return luaL_error(L, "unsupported terminal");
case TB_EFAILED_TO_OPEN_TTY: return luaL_error(L, "failed to open tty");
case TB_EPIPE_TRAP_ERROR: return luaL_error(L, "sigpipe trap");
default: return luaL_error(L, "unknown error");
}
} else
initialized = 1;
return 0;
}
/* helper: shutdown termfx. Also used for atexit.
*/
static void _tfx_doShutdown(void)
{
if (initialized)
tb_shutdown();
initialized = 0;
}
/* tfx_shutdown
*
* shutdown TermFX
*
* Arguments:
* L Lua State
*
* Lua Stack:
* -
*
* Lua Returns:
* -
*/
static int tfx_shutdown(lua_State *L)
{
maxargs(L, 0);
_tfx_doShutdown();
return 0;
}
/* tfx_width
*
* returns width of terminal
*
* Arguments:
* L Lua State
*
* Lua Stack:
* -
*
* Lua Returns:
* +1 width of terminal, or nil if the terminal is not initialized.
*/
static int tfx_width(lua_State *L)
{
maxargs(L, 0);
int w = tb_width();
if (w >= 0)
lua_pushinteger(L, w);
else
lua_pushnil(L);
return 1;
}
/* tfx_height
*
* returns height of terminal
*
* Arguments:
* L Lua State
*
* Lua Stack:
* -
*
* Lua Returns:
* +1 height of terminal, or nil if the terminal is not initialized.
*/
static int tfx_height(lua_State *L)
{
maxargs(L, 0);
int h = tb_height();
if (h >= 0)
lua_pushinteger(L, h);
else
lua_pushnil(L);
return 1;
}
/* tfx_size
*
* returns width and height of terminal
*
* Arguments:
* L Lua State
*
* Lua Stack:
* -
*
* Lua Returns:
* +1 width of terminal, or nil if the terminal is not initialized.
* +2 height of terminal, or nil if the terminal is not initialized.
*/
static int tfx_size(lua_State *L)
{
maxargs(L, 0);
int w = tb_width(), h = tb_height();
if (w >= 0) {
lua_pushinteger(L, w);
lua_pushinteger(L, h);
} else {
lua_pushnil(L);
lua_pushnil(L);
}
return 2;
}
/* tfx_attributes
*
* sets and gets default foreground and background attributes for terminal
* operations
*
* Arguments:
* L Lua State
*
* Lua Stack:
* 1 (opt) foreground attribute
* 2 (opt) background attribute
*
* Lua Returns:
* +1 default foreground attribute, or nothing if terminal is not initialized
* +2 default background attribute, or nothing if terminal is not initialized
*/
static int tfx_attributes(lua_State *L)
{
maxargs(L, 2);
if (!initialized) return 0;
if (lua_gettop(L) > 0) {
default_fg = (uint16_t) luaL_optinteger(L, 1, default_fg) & 0xFFFF;
default_bg = (uint16_t) luaL_optinteger(L, 2, default_bg) & 0xFFFF;
tb_set_clear_attributes(default_fg, default_bg);
}
lua_pushinteger(L, default_fg);
lua_pushinteger(L, default_bg);
return 2;
}
/* tfx_clear
*
* clears the terminal to the default foreground and background attributes.
* If the optional foreground and background attribute arguments are
* given, these will be set using tfx_bufSetAttributes before clearing.
*
* Arguments:
* L Lua State
*
* Lua Stack:
* 1 TfxBuffer userdata
* 2 (opt) foreground attributes, defaults to buffer default foreground
* 3 (opt) background attributes, defaults to buffer default background
*
* Lua Returns:
* -
*/
static int tfx_clear(lua_State *L)
{
if (lua_gettop(L) != 0)
tfx_attributes(L);
if (initialized) tb_clear();
return 0;
}
/* tfx_present
*
* update the terminal with the data from the back buffer
*
* Arguments:
* L Lua State
*
* Lua Stack:
* -
*
* Lua Returns:
* -
*/
static int tfx_present(lua_State *L)
{
maxargs(L, 0);
if (initialized) tb_present();
return 0;
}
/* tfx_setCursor
*
* set cursor position
*
* Arguments:
* L Lua State
*
* Lua Stack:
* 1 x coordinate
* 2 y coordinate
*
* Lua Returns:
* -
*/
static int tfx_setCursor(lua_State *L)
{
maxargs(L, 2);
int x = (int) luaL_checkinteger(L, 1) - top_left_coord;
int y = (int) luaL_checkinteger(L, 2) - top_left_coord;
if (initialized) tb_set_cursor(x, y);
return 0;
}
/* tfx_hideCursor
*
* hide cursor
*
* Arguments:
* L Lua State
*
* Lua Stack:
* -
*
* Lua Returns:
* -
*/
static int tfx_hideCursor(lua_State *L)
{
maxargs(L, 0);
if (initialized) tb_set_cursor(TB_HIDE_CURSOR, TB_HIDE_CURSOR);
return 0;
}
/* tfx_setcell
*
* sets a cell on the terminal. Either uses the default values for foreground
* and background, or the values passed on the lua stack.
*
* Arguments:
* L Lua State
*
* Lua Stack:
* 1 x coordinate of cell
* 2 y coordinate of cell
* either
* 3 TfxCell
* or
* 3 (opt) char, defaults to ' '
* 4 (opt) foreground attributes, defaults to buffer default foreground
* 5 (opt) background attributes, defaults to buffer default background
*
* Lua Returns:
* -
*/
static int tfx_setCell(lua_State *L)
{
int x = (int) luaL_checkinteger(L, 1) - top_left_coord;
int y = (int) luaL_checkinteger(L, 2) - top_left_coord;
if (tfx_isCell(L, 3)) {
maxargs(L, 3);
const TfxCell *tfxcell = tfx_checkCell(L, 3);
if (initialized) tb_put_cell(x, y, tfxcell);
} else {
maxargs(L, 5);
uint32_t ch = _tfx_optchar(L, 3, ' ');
uint16_t fg = (uint16_t) luaL_optinteger(L, 4, default_fg) & 0xFFFF;
uint16_t bg = (uint16_t) luaL_optinteger(L, 5, default_bg) & 0xFFFF;
if (ch < ' ') ch = ' ';
if (initialized) tb_change_cell(x, y, ch, fg, bg);
}
return 0;
}
/* tfx_getCell
*
* gets data from a cell on the terminal
*
* Arguments:
* L Lua State
*
* Lua Stack:
* 1 x coordinate of cell
* 2 y coordinate of cell
*
* Lua Returns:
* a TfxCell with the data from the read cell
*/
int tfx_getCell(lua_State *L)
{
int x = (int) luaL_checkinteger(L, 1) - top_left_coord;
int y = (int) luaL_checkinteger(L, 2) - top_left_coord;
if (x < 0 || x >= tb_width() || y < 0 || y >= tb_height()) {
lua_pushnil(L);
return 1;
}
TfxCell *tfxcell = tfx_pushCell(L);
*tfxcell = CELL(tb_cell_buffer(), tb_width(), x, y);
return 1;
}
/* tfx_blit
*
* blits the contents of a buffer to the terminal.
*
* Arguments:
* L Lua State
*
* Lua Stack:
* 1 x coordinate of cell
* 2 y coordinate of cell
* 3 TfxBuffer
*
* Lua Returns:
* -
*/
static int tfx_blit(lua_State *L)
{
maxargs(L, 3);
int x = (int) luaL_checkinteger(L, 1) - top_left_coord;
int y = (int) luaL_checkinteger(L, 2) - top_left_coord;
TfxBuffer *tfxbuf = tfx_checkBuffer(L, 3);
if (initialized) tbu_blit(x, y, tfxbuf->buf, tfxbuf->w, tfxbuf->h);
return 0;
}
/* tfx_rect
*
* draws a rectangle. Either uses the default values for foreground and
* background, or the values passed on the lua stack.
*
* Arguments:
* L Lua State
*
* Lua Stack:
* 1 x coordinate of rect
* 2 y coordinate of rect
* 3 width of rect
* 4 height of rect
* either
* 5 TfxCell
* or
* 5 (opt) char, defaults to ' '
* 6 (opt) foreground attributes, defaults to buffer default foreground
* 7 (opt) background attributes, defaults to buffer default background
*
* Lua Returns:
* false if the rectangle was entirely outside of the terminal, true if not
*/
static int tfx_rect(lua_State *L)
{
if (!initialized) return 0;
TfxCell cel, *celp = &cel;
int x = (int) luaL_checkinteger(L, 1) - top_left_coord;
int y = (int) luaL_checkinteger(L, 2) - top_left_coord;
int w = (int) luaL_checkinteger(L, 3);
int h = (int) luaL_checkinteger(L, 4);
if (tfx_isCell(L, 5)) {
maxargs(L, 5);
celp = tfx_toCell(L, 5);
} else {
maxargs(L, 7);
cel.ch = _tfx_optchar(L, 5, 0);
cel.fg = (uint16_t) luaL_optinteger(L, 6, default_fg) & 0xFFFF;
cel.bg = (uint16_t) luaL_optinteger(L, 7, default_bg) & 0xFFFF;
}
tbu_fillregion(x, y, w, h, celp);
return 0;
}
/* tfx_copyRegion
*
* copies a rectangular region to another place
*
* Arguments:
* L Lua State
*
* Lua Stack
* 1 target x coordinate for copy
* 2 target y coordinate for copy
* 3 source x coordinate
* 4 source y coordinate
* 5 width of rectangle to copy
* 6 height of rectangle to copy
*
* Lua Returns:
* -
*/
int tfx_copyRegion(lua_State *L)
{
if (!initialized) return 0;
int tx = (int) luaL_checkinteger(L, 1) - top_left_coord;
int ty = (int) luaL_checkinteger(L, 2) - top_left_coord;
int x = (int) luaL_checkinteger(L, 3) - top_left_coord;
int y = (int) luaL_checkinteger(L, 4) - top_left_coord;
int w = (int) luaL_checkinteger(L, 5);
int h = (int) luaL_checkinteger(L, 6);
tbu_copyregion(tx, ty, x, y, w, h);
return 0;
}
/* tfx_scrollRegion
*
* scrolls a rectangular region
*
* Arguments:
* L Lua State
*
* Lua Stack
* 1 x coordinate of rectangle to scroll
* 2 y coordinate of rectangle to scroll
* 3 width of rectangle to scroll
* 4 height of rectangle to scroll
* 5 (opt) x scroll direction (-1, 0, 1), defaults to 0
* 6 (opt) y scroll direction (-1, 0, 1), defaults to 0
* either
* 7 TfxCell
* or
* 7 (opt) char, defaults to ' '
* 8 (opt) foreground attributes, defaults to buffer default foreground
* 9 (opt) background attributes, defaults to buffer default background
*
* Lua Returns:
* -
*
* Note:
* arguments 5 and 6 (scroll directions) are ints, but only their sign is
* used. Scrolling is always by at most 1 cell.
*/
int tfx_scrollRegion(lua_State *L)
{
if (!initialized) return 0;
TfxCell cel, *celp = &cel;
int x = (int) luaL_checkinteger(L, 1) - top_left_coord;
int y = (int) luaL_checkinteger(L, 2) - top_left_coord;
int w = (int) luaL_checkinteger(L, 3);
int h = (int) luaL_checkinteger(L, 4);
int sx = (int) luaL_optinteger(L, 5, 0);
int sy = (int) luaL_optinteger(L, 6, 0);
if (tfx_isCell(L, 7)) {
maxargs(L, 7);
celp = tfx_toCell(L, 7);
} else {
maxargs(L, 9);
cel.ch = _tfx_optchar(L, 7, 0);
cel.fg = (uint16_t) luaL_optinteger(L, 8, default_fg) & 0xFFFF;
cel.bg = (uint16_t) luaL_optinteger(L, 9, default_bg) & 0xFFFF;
}
tbu_scrollregion(x, y, w, h, sx, sy, celp);
return 0;
}
/* tfx_printAt
*
* prints some text to the top left position at x, y. Is used as both the
* function termfx.printat and the TfxBuffer printat method, depending on
* the first argument.
*
* Arguments:
* L Lua State
*
* Lua Stack:
* (1 TfxBuffer)
* +1 x coordinate of text
* +2 y coordinate of text
* +3 text (string or table of chars)
* +4 (opt) maximum width to print
*
* Lua Returns:
* -
*/
static int tfx_printAt(lua_State *L)
{
if (!initialized) return 0;
TfxBuffer *tfxbuf = NULL;
int fw, start = 1;
uint16_t dfg = default_fg, dbg = default_bg;
if (tfx_isBuffer(L, 1)) {
tfxbuf = tfx_checkBuffer(L, 1);
fw = tfxbuf->w;
dfg = tfxbuf->fg;
dbg = tfxbuf->bg;
start = 2;
} else {
fw = tb_width();
}
int x = (int) luaL_checkinteger(L, start) - top_left_coord;
int y = (int) luaL_checkinteger(L, start + 1) - top_left_coord;
int pw = -1;
if (lua_gettop(L) == start + 3)
pw = (int) luaL_checkinteger(L, start + 3);
if (lua_type(L, start + 2) == LUA_TTABLE) {
int i = 1;
int w = 0;
if (pw < 0) {
pw = lua_rawlen(L, start + 2);
}
lua_rawgeti(L, start + 2, i);
while (!lua_isnil(L, -1) && w < pw && x + w < fw) {
TfxCell *c = tfx_toCell(L, -1);
if (c) {
if (tfxbuf)
_tfx_bufChangeCell(tfxbuf, x + w, y, c);
else
tb_put_cell(x + w, y, c);
}
++w;
++i;
lua_rawgeti(L, start + 2, i);
}
} else if (!lua_isnil(L, start + 2)) {
const char* str = luaL_checkstring(L, start + 2);
int isutf8 = mini_utf8_check_encoding(str) == 0;
int w = 0;
if (pw < 0) {
pw = isutf8 ? mini_utf8_strlen(str) : strlen(str);
}
struct tb_cell c;
c.fg = dfg;
c.bg = dbg;
c.ch = isutf8 ? mini_utf8_decode(&str) : *str++;
if (c.ch < ' ' && c.ch > 0) c.ch = ' ';
for (w = 0; c.ch && (w < pw) && (x + w < fw); ++w) {
if (tfxbuf)
_tfx_bufChangeCell(tfxbuf, x + w, y, &c);
else
tb_put_cell(x + w, y, &c);
c.ch = isutf8 ? mini_utf8_decode(&str) : *str++;
if (c.ch < ' ' && c.ch > 0) c.ch = ' ';
}
}
return 0;
}
/* tfx_inputMode
*
* set or query input mode
*
* Arguments:
* L Lua State
*
* Lua Stack:
* 1 input mode, or nil to query
*
* Lua Returns:
* +1 current input mode
*/
static int tfx_inputMode(lua_State *L)
{
maxargs(L, 1);
int mode = TB_INPUT_CURRENT;
if (!lua_isnoneornil(L, 1))
mode = (int) luaL_checkinteger(L, 1);
int res = tb_select_input_mode(mode);
lua_pushinteger(L, res);
return 1;
}
/* tfx_outputMode
*
* set or query output mode
*
* Arguments:
* L Lua State
*
* Lua Stack:
* 1 output mode, or nil to query
*
* Lua Returns:
* +1 current output mode
*/
static int tfx_outputMode(lua_State *L)
{
maxargs(L, 1);
int mode = TB_OUTPUT_CURRENT;
if (!lua_isnoneornil(L, 1))
mode = (int) luaL_checkinteger(L, 1);
int res = tb_select_output_mode(mode);
lua_pushinteger(L, res);
return 1;
}
/* tfx_pollEvent
*
* polls next event.
*
* Arguments:
* L Lua State
*
* Lua Stack:
* 1 (opt) timeout in ms, waits forever for the next event if not set
*
* Lua Returns:
* +1 a table with the event data
*/
static int tfx_pollEvent(lua_State *L)
{
maxargs(L, 1);
struct tb_event evt;
int eno = 0;
uint32_t started = _tfx_getMsTimer();
if (lua_gettop(L) == 1 && !lua_isnil(L, 1)) {
uint32_t timeout = (int) luaL_checkinteger(L, 1);
eno = initialized ? tb_peek_event(&evt, timeout) : 0;
} else
eno = initialized ? tb_poll_event(&evt) : 0;
if (eno > 0) {
lua_newtable(L);
lua_pushliteral(L, "type");
switch (evt.type) {
case TB_EVENT_KEY: lua_pushliteral(L, "key"); break;
case TB_EVENT_RESIZE: lua_pushliteral(L, "resize"); break;
#ifdef TB_EVENT_MOUSE
case TB_EVENT_MOUSE: lua_pushliteral(L, "mouse"); break;
#endif
default: lua_pushliteral(L, "unknown");
}
lua_rawset(L, -3);
lua_pushliteral(L, "elapsed");
lua_pushinteger(L, _tfx_getMsTimer() - started);
lua_rawset(L, -3);
if (evt.type == TB_EVENT_KEY) {
lua_pushliteral(L, "mod");
switch (evt.mod) {
case TB_MOD_ALT: lua_pushliteral(L, "ALT"); break;
default: lua_pushnil(L);
}
lua_rawset(L, -3);
lua_pushliteral(L, "key");
if (evt.ch == 0) {
lua_pushinteger(L, evt.key);
} else {
lua_pushnil(L);
}
lua_rawset(L, -3);
lua_pushliteral(L, "ch");
if (evt.ch != 0) {
lua_pushinteger(L, evt.ch);
} else {
lua_pushnil(L);
}
lua_rawset(L, -3);
lua_pushliteral(L, "char");
char c[10];
memset(c, 0, 10);
if (evt.ch < 0x7F)
c[0] = evt.ch;
else
mini_utf8_encode(evt.ch, c, 10);
lua_pushstring(L, c);
lua_rawset(L, -3);
} else if (evt.type == TB_EVENT_RESIZE) {
lua_pushliteral(L, "w");
lua_pushinteger(L, evt.w);
lua_rawset(L, -3);
lua_pushliteral(L, "h");
lua_pushinteger(L, evt.h);
lua_rawset(L, -3);
#ifdef TB_EVENT_MOUSE
} else if (evt.type == TB_EVENT_MOUSE) {
lua_pushliteral(L, "x");
lua_pushinteger(L, evt.x + top_left_coord);
lua_rawset(L, -3);
lua_pushliteral(L, "y");
lua_pushinteger(L, evt.y + top_left_coord);
lua_rawset(L, -3);
lua_pushliteral(L, "key");
lua_pushinteger(L, evt.key);
lua_rawset(L, -3);
#endif
}
} else if (eno == 0) {
lua_pushnil(L);
} else {
lua_pushnil(L);
lua_pushstring(L, strerror(errno));
return 2;
}
return 1;
}
/* methods for the TfxBuffer userdata
*/
static const struct luaL_Reg ltfxbuf_methods [] = {
{"attributes", tfx_bufAttributes},
{"clear", tfx_bufClear},
{"setcell", tfx_bufSetcell},
{"getcell", tfx_bufGetCell},
{"blit", tfx_bufBlit},
{"rect", tfx_bufRect},
{"copyregion", tfx_bufCopyRegion},
{"scrollregion", tfx_bufScrollRegion},
{"printat", tfx_printAt},
{"width", tfx_bufWidth},
{"height", tfx_bufHeight},
{"size", tfx_bufSize},
{NULL, NULL}
};
/* TermFX function list
*/
static const struct luaL_Reg ltermfx [] ={
{"init", tfx_init},
{"shutdown", tfx_shutdown},
{"width", tfx_width},
{"height", tfx_height},
{"size", tfx_size},
{"clear", tfx_clear},
{"attributes", tfx_attributes},
{"present", tfx_present},
{"setcursor", tfx_setCursor},
{"hidecursor", tfx_hideCursor},
{"newcell", tfx_newCell},
{"setcell", tfx_setCell},
{"getcell", tfx_getCell},
{"newbuffer", tfx_newBuffer},
{"blit", tfx_blit},
{"rect", tfx_rect},
{"copyregion", tfx_copyRegion},
{"scrollregion", tfx_scrollRegion},
{"printat", tfx_printAt},
{"inputmode", tfx_inputMode},
{"outputmode", tfx_outputMode},
{"pollevent", tfx_pollEvent},
{NULL, NULL}
};
/* helper: __index metamethod for the table containing the color values
* for the predefined color names. As the values for these names are not
* constant across the output modes, some tweaking is needed.
*/
static int tfx__indexColor(lua_State *L)
{
const char* what = luaL_checkstring(L, 2);
int omode = tb_select_output_mode(TB_OUTPUT_CURRENT);
int col = -1;
if (!strcmp("DEFAULT", what)) col = TB_DEFAULT;
else if (!strcmp("BLACK", what)) col = TB_BLACK;
else if (!strcmp("RED", what)) col = TB_RED;
else if (!strcmp("GREEN", what)) col = TB_GREEN;
else if (!strcmp("YELLOW", what)) col = TB_YELLOW;
else if (!strcmp("BLUE", what)) col = TB_BLUE;
else if (!strcmp("MAGENTA", what)) col = TB_MAGENTA;
else if (!strcmp("CYAN", what)) col = TB_CYAN;
else if (!strcmp("WHITE", what)) col = TB_WHITE;
switch (omode) {
case TB_OUTPUT_256:
col -= 1;
break;
case TB_OUTPUT_GRAYSCALE:
col = (col - 1) * 3;
if (col > 6) col += 1;
if (col > 15) col += 1;
break;
case TB_OUTPUT_216:
switch (col) {
case TB_BLACK: col = 0; break;
case TB_RED: col = 72; break;
case TB_GREEN: col = 12; break;
case TB_YELLOW: col = 114; break;
case TB_BLUE: col = 2; break;
case TB_MAGENTA: col = 74; break;
case TB_CYAN: col = 14; break;
case TB_WHITE: col = 129; break;
default:;
}
break;
default:;
}
if (col < 0)
lua_pushnil(L);
else
lua_pushinteger(L, col);
return 1;
}
/* add constants */
#define ADDCONST(n, v) \
lua_pushliteral(L, n); \
lua_pushinteger(L, v); \
lua_rawset(L, -3)
static void tfx_addconstants(lua_State *L)
{
lua_pushliteral(L, "key");
lua_newtable(L);
ADDCONST("F1", TB_KEY_F1);
ADDCONST("F2", TB_KEY_F2);
ADDCONST("F3", TB_KEY_F3);
ADDCONST("F4", TB_KEY_F4);
ADDCONST("F5", TB_KEY_F5);
ADDCONST("F6", TB_KEY_F6);
ADDCONST("F7", TB_KEY_F7);
ADDCONST("F8", TB_KEY_F8);
ADDCONST("F9", TB_KEY_F9);
ADDCONST("F10", TB_KEY_F10);
ADDCONST("F11", TB_KEY_F11);
ADDCONST("F12", TB_KEY_F12);
ADDCONST("INSERT", TB_KEY_INSERT);
ADDCONST("DELETE", TB_KEY_DELETE);
ADDCONST("HOME", TB_KEY_HOME);
ADDCONST("END", TB_KEY_END);
ADDCONST("PGUP", TB_KEY_PGUP);
ADDCONST("PGDN", TB_KEY_PGDN);
ADDCONST("ARROW_UP", TB_KEY_ARROW_UP);
ADDCONST("ARROW_DOWN", TB_KEY_ARROW_DOWN);
ADDCONST("ARROW_LEFT", TB_KEY_ARROW_LEFT);
ADDCONST("ARROW_RIGHT", TB_KEY_ARROW_RIGHT);
#ifdef TB_INPUT_MOUSE
ADDCONST("MOUSE_LEFT", TB_KEY_MOUSE_LEFT);
ADDCONST("MOUSE_RIGHT", TB_KEY_MOUSE_RIGHT);
ADDCONST("MOUSE_MIDDLE", TB_KEY_MOUSE_MIDDLE);
ADDCONST("MOUSE_RELEASE", TB_KEY_MOUSE_RELEASE);
ADDCONST("MOUSE_WHEEL_UP", TB_KEY_MOUSE_WHEEL_UP);
ADDCONST("MOUSE_WHEEL_DOWN", TB_KEY_MOUSE_WHEEL_DOWN);
#endif
ADDCONST("CTRL_TILDE", TB_KEY_CTRL_TILDE);
ADDCONST("CTRL_2", TB_KEY_CTRL_2);
ADDCONST("CTRL_A", TB_KEY_CTRL_A);
ADDCONST("CTRL_B", TB_KEY_CTRL_B);
ADDCONST("CTRL_C", TB_KEY_CTRL_C);
ADDCONST("CTRL_D", TB_KEY_CTRL_D);
ADDCONST("CTRL_E", TB_KEY_CTRL_E);
ADDCONST("CTRL_F", TB_KEY_CTRL_F);
ADDCONST("CTRL_G", TB_KEY_CTRL_G);
ADDCONST("BACKSPACE", TB_KEY_BACKSPACE);
ADDCONST("CTRL_H", TB_KEY_CTRL_H);
ADDCONST("TAB", TB_KEY_TAB);
ADDCONST("CTRL_I", TB_KEY_CTRL_I);
ADDCONST("CTRL_J", TB_KEY_CTRL_J);
ADDCONST("CTRL_K", TB_KEY_CTRL_K);
ADDCONST("CTRL_L", TB_KEY_CTRL_L);
ADDCONST("ENTER", TB_KEY_ENTER);
ADDCONST("CTRL_M", TB_KEY_CTRL_M);
ADDCONST("CTRL_N", TB_KEY_CTRL_N);
ADDCONST("CTRL_O", TB_KEY_CTRL_O);
ADDCONST("CTRL_P", TB_KEY_CTRL_P);
ADDCONST("CTRL_Q", TB_KEY_CTRL_Q);
ADDCONST("CTRL_R", TB_KEY_CTRL_R);
ADDCONST("CTRL_S", TB_KEY_CTRL_S);
ADDCONST("CTRL_T", TB_KEY_CTRL_T);
ADDCONST("CTRL_U", TB_KEY_CTRL_U);
ADDCONST("CTRL_V", TB_KEY_CTRL_V);
ADDCONST("CTRL_W", TB_KEY_CTRL_W);
ADDCONST("CTRL_X", TB_KEY_CTRL_X);
ADDCONST("CTRL_Y", TB_KEY_CTRL_Y);
ADDCONST("CTRL_Z", TB_KEY_CTRL_Z);
ADDCONST("ESC", TB_KEY_ESC);
ADDCONST("CTRL_LSQ_BRACKET", TB_KEY_CTRL_LSQ_BRACKET);
ADDCONST("CTRL_3", TB_KEY_CTRL_3);
ADDCONST("CTRL_4", TB_KEY_CTRL_4);
ADDCONST("CTRL_BACKSLASH", TB_KEY_CTRL_BACKSLASH);
ADDCONST("CTRL_5", TB_KEY_CTRL_5);
ADDCONST("CTRL_RSQ_BRACKET", TB_KEY_CTRL_RSQ_BRACKET);
ADDCONST("CTRL_6", TB_KEY_CTRL_6);
ADDCONST("CTRL_7", TB_KEY_CTRL_7);
ADDCONST("CTRL_SLASH", TB_KEY_CTRL_SLASH);
ADDCONST("CTRL_UNDERSCORE", TB_KEY_CTRL_UNDERSCORE);
ADDCONST("SPACE", TB_KEY_SPACE);
ADDCONST("BACKSPACE2", TB_KEY_BACKSPACE2);
ADDCONST("CTRL_8", TB_KEY_CTRL_8);
lua_rawset(L, -3);
lua_pushliteral(L, "color");
lua_newtable(L);
lua_newtable(L);
lua_pushliteral(L, "__index");
lua_pushcfunction(L, tfx__indexColor);
lua_rawset(L, -3);
lua_setmetatable(L, -2);
lua_rawset(L, -3);
lua_pushliteral(L, "format");
lua_newtable(L);
ADDCONST("BOLD", TB_BOLD);
ADDCONST("UNDERLINE", TB_UNDERLINE);
ADDCONST("REVERSE", TB_REVERSE);
lua_rawset(L, -3);
lua_pushliteral(L, "input");
lua_newtable(L);
ADDCONST("ESC", TB_INPUT_ESC);
ADDCONST("ALT", TB_INPUT_ALT);
#ifdef TB_INPUT_MOUSE
ADDCONST("MOUSE", TB_INPUT_MOUSE);
#endif
lua_rawset(L, -3);
lua_pushliteral(L, "output");
lua_newtable(L);
ADDCONST("NORMAL", TB_OUTPUT_NORMAL);
ADDCONST("COL256", TB_OUTPUT_256);
ADDCONST("COL216", TB_OUTPUT_216);
ADDCONST("GRAYSCALE", TB_OUTPUT_GRAYSCALE);
ADDCONST("GREYSCALE", TB_OUTPUT_GRAYSCALE);
lua_rawset(L, -3);
}
/* luaopen_ltermbox
*
* open and initialize this library
*/
int luaopen_termfx(lua_State *L)
{
struct timeval tv;
gettimeofday(&tv, NULL);
mstimer_tm0 = tv.tv_sec * 1000 + tv.tv_usec / 1000;
luaL_newlib(L, ltermfx);
tfx_color_init(L);
tfx_addconstants(L);
lua_pushliteral(L, "_VERSION");
lua_pushliteral(L, _VERSION);
lua_rawset(L, -3);
luaL_newmetatable(L, TFXCELL);
luaL_setfuncs(L, tfx_CellMeta, 0);
lua_pop(L, 1);
luaL_newmetatable(L, TFXBUFFER);
luaL_setfuncs(L, tfx_BufferMeta, 0);
lua_pushliteral(L, "__index");
luaL_newlib(L, ltfxbuf_methods);
lua_rawset(L, -3);
lua_pop(L, 1);
/* shutdown termbox or suffer the consequences */
atexit(_tfx_doShutdown);
return 1;
}