#include <ncurses.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "lua.h"
#include "llimits.h"
#include "lauxlib.h"
#include "lualib.h"
void cleanup_curses (void) {
if (!isendwin()) {
wclear(stdscr);
wrefresh(stdscr);
endwin();
}
}
int menu_column = 0;
void draw_string_on_menu (const char* s) {
mvaddstr(LINES-1, menu_column, " ");
++menu_column;
mvaddstr(LINES-1, menu_column, s);
menu_column += strlen(s);
mvaddstr(LINES-1, menu_column, " ");
++menu_column;
}
void draw_menu_item (const char* key, const char* name) {
attroff(A_REVERSE);
draw_string_on_menu(key);
attron(A_REVERSE);
draw_string_on_menu(name);
}
void draw_menu (lua_State *L) {
attron(A_BOLD|A_REVERSE);
for (int x = 0; x < COLS; ++x)
mvaddch(LINES-1, x, ' ');
menu_column = 2;
draw_menu_item("^x", "exit");
draw_menu_item("^e", "edit");
/* render any app-specific items */
lua_getglobal(L, "menu");
int table = lua_gettop(L);
if (lua_istable(L, -1))
for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1))
draw_menu_item(lua_tostring(L, -2), lua_tostring(L, -1));
lua_pop(L, 1);
attroff(A_BOLD|A_REVERSE);
}
static int Prefresh (lua_State *L) {
refresh();
draw_menu(L);
return 1;
}
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 void checktype (lua_State *L, int narg, int t, const char *expected) {
if (lua_type(L, narg) != t)
argtypeerror(L, narg, expected);
}
static lua_Integer checkinteger (lua_State *L, int narg, const char *expected) {
lua_Integer d = lua_tointeger(L, narg);
if (d == 0 && !lua_isnumber(L, narg))
argtypeerror(L, narg, expected);
return d;
}
static int Pstdscr (lua_State *L) {
lua_pushstring(L, "curses:stdscr");
lua_rawget(L, LUA_REGISTRYINDEX);
return 1;
}
static int Pcolor_pairs (lua_State *L) {
lua_pushinteger(L, COLOR_PAIRS);
return 1;
}
static int Pinit_pair (lua_State *L) {
int pair = checkinteger(L, 1, "int");
short f = checkinteger(L, 2, "int");
short b = checkinteger(L, 3, "int");
init_pair(pair, f, b);
return 1;
}
static int Pcolor_pair (lua_State *L)
{
int n = checkinteger(L, 1, "int");
lua_pushinteger(L, COLOR_PAIR(n));
return 1;
}
extern void switch_to_editor (lua_State *L, const char *message);
static int Pgetch (lua_State *L) {
int c = wgetch(stdscr);
if (c == ERR)
return 0;
if (c == 24) /* ctrl-x */
exit(0);
if (c == 5) /* ctrl-e */
switch_to_editor(L, "");
/* handle other standard menu hotkeys here */
lua_pushinteger(L, c);
return 1;
}
static const struct luaL_Reg curseslib [] = {
{"color_pairs", Pcolor_pairs},
{"color_pair", Pcolor_pair},
{"getch", Pgetch},
{"init_pair", Pinit_pair},
{"refresh", Prefresh},
{"stdscr", Pstdscr},
{NULL, NULL}
};
static void curses_newwin (lua_State *L, WINDOW *nw) {
if (nw) {
WINDOW **w = lua_newuserdata(L, sizeof(WINDOW*));
luaL_getmetatable(L, "curses:window");
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, "curses:window");
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;
}
static chtype checkch (lua_State *L, int narg) {
if (lua_isnumber(L, narg))
return cast(chtype, checkinteger(L, narg, "int"));
if (lua_isstring(L, narg))
return *lua_tostring(L, narg);
return argtypeerror(L, narg, "int or char");
}
static int optint (lua_State *L, int narg, lua_Integer def) {
if (lua_isnoneornil(L, narg))
return cast(int, def);
return cast(int, checkinteger(L, narg, "int or nil"));
}
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;
}
static int Waddstr (lua_State *L) {
WINDOW *w = checkwin(L, 1);
const char *str = luaL_checkstring(L, 2);
int n = optint(L, 3, -1);
lua_pushboolean(L, waddnstr(w, str, n));
return 1;
}
static int Wattroff (lua_State *L) {
WINDOW *w = checkwin(L, 1);
int attrs = checkinteger(L, 2, "int");
lua_pushboolean(L, wattroff(w, attrs));
return 1;
}
static int Wattron (lua_State *L) {
WINDOW *w = checkwin(L, 1);
int attrs = checkinteger(L, 2, "int");
lua_pushboolean(L, wattron(w, attrs));
return 1;
}
static int Wclear (lua_State *L) {
lua_pushboolean(L, wclear(checkwin(L, 1)));
return 1;
}
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;
}
static int Wgetmaxyx (lua_State *L) {
WINDOW *w = checkwin(L, 1);
int y, x;
getmaxyx(w, y, x);
--y; // set aside space for the menu bar
lua_pushinteger(L, y);
lua_pushinteger(L, x);
return 2;
}
static int Wmvaddch (lua_State *L) {
WINDOW *w = checkwin(L, 1);
int y = checkinteger(L, 2, "int");
int x = checkinteger(L, 3, "int");
chtype ch = checkch(L, 4);
mvwaddch(w, y, x, ch);
return 1;
}
static int Wmvaddstr (lua_State *L) {
WINDOW *w = checkwin(L, 1);
int y = checkinteger(L, 2, "int");
int x = checkinteger(L, 3, "int");
const char *str = luaL_checkstring(L, 4);
int n = optint(L, 5, -1);
mvwaddnstr(w, y, x, str, n);
return 1;
}
static int Wnodelay (lua_State *L) {
WINDOW *w = checkwin(L, 1);
checktype(L, 2, LUA_TBOOLEAN, "boolean or nil");
int bf = (int)lua_toboolean(L, 2);
lua_pushboolean(L, nodelay(w, bf));
return 1;
}
static const luaL_Reg curses_window_methods[] =
{
{"__tostring", W__tostring},
{"addstr", Waddstr},
{"attroff", Wattroff},
{"attron", Wattron},
{"clear", Wclear},
{"getmaxyx", Wgetmaxyx},
{"getyx", Wgetyx},
{"mvaddch", Wmvaddch},
{"mvaddstr", Wmvaddstr},
{"nodelay", Wnodelay},
{NULL, NULL}
};
static void register_curses_constant (lua_State *L, const char* name, int val) {
lua_pushstring(L, name);
lua_pushinteger(L, val);
lua_settable(L, -3);
}
static void register_curses_constants (lua_State *L) {
#define CC(s) register_curses_constant(L, #s, s)
/* 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);
CC(A_COLOR);
/* 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);
#undef CC
}
LUALIB_API int luaopen_curses (lua_State *L) {
luaL_newmetatable(L, "curses:window");
/* metatable.__index = metatable */
lua_pushstring(L, "__index");
lua_pushvalue(L, -2);
lua_settable(L, -3);
luaL_register(L, NULL, curses_window_methods);
luaL_register(L, "curses", curseslib);
/* save main window on registry */
curses_newwin(L, stdscr);
lua_pushstring(L, "curses:stdscr");
lua_pushvalue(L, -2);
lua_rawset(L, LUA_REGISTRYINDEX);
lua_pushvalue(L, -2);
register_curses_constants(L);
atexit(cleanup_curses);
return 1;
}