#include "strings.h" #include #include #include #include #include #define REG_TABLE "luancurses" #define NO_ARG_FUNCTION(name) \ static int l_##name(lua_State* L) \ { \ lua_pushboolean(L, name() == OK); \ return 1; \ } typedef struct _pos { int x; int y; } pos; static int ncolors = 0, ncolor_pairs = 0, default_color_available = 0; static int get_color_pair(lua_State* L, const char* str) { int ret = -1; lua_getfield(L, LUA_REGISTRYINDEX, REG_TABLE); lua_getfield(L, -1, "color_pairs"); lua_pushnil(L); while (lua_next(L, -2) != 0) { if (lua_isstring(L, -2)) { const char* key; key = lua_tostring(L, -2); if (!strcmp(key, str)) { ret = lua_tointeger(L, -1); lua_pop(L, 2); break; } } lua_pop(L, 1); } lua_pop(L, 2); return ret; } static void init_color_pairs(lua_State* L) { lua_getfield(L, LUA_REGISTRYINDEX, REG_TABLE); lua_newtable(L); lua_pushinteger(L, 0); lua_setfield(L, -2, "default"); lua_setfield(L, -2, "color_pairs"); lua_pop(L, 1); } static void register_color(const char* color_str, int color_tag, void* data) { lua_pushinteger((lua_State*)data, color_tag); lua_setfield((lua_State*)data, -2, color_str); ncolors++; } static void register_default_color(lua_State* L) { lua_getfield(L, LUA_REGISTRYINDEX, REG_TABLE); lua_getfield(L, -1, "colors"); lua_pushinteger(L, -1); lua_setfield(L, -2, "default"); lua_pop(L, 2); default_color_available = 1; } static void init_colors(lua_State* L) { lua_getfield(L, LUA_REGISTRYINDEX, REG_TABLE); lua_newtable(L); each_color(register_color, L); lua_setfield(L, -2, "colors"); lua_pop(L, 1); } static int get_pos(lua_State* L, pos* p) { if (!lua_istable(L, 1)) { return 0; } getyx(stdscr, p->y, p->x); lua_getfield(L, 1, "x"); if (lua_isnumber(L, -1)) { p->x = lua_tonumber(L, -1); } lua_pop(L, 1); lua_getfield(L, 1, "y"); if (lua_isnumber(L, -1)) { p->y = lua_tonumber(L, -1); } lua_pop(L, 1); lua_remove(L, 1); return 1; } static int get_char_color(lua_State* L, int stack_pos) { const char* str; int val = -1; lua_getfield(L, stack_pos, "color"); if (!lua_isstring(L, -1)) { return 0; } str = lua_tostring(L, -1); lua_pop(L, 1); val = get_color_pair(L, str); if (val == -1) { return luaL_error(L, "Unknown color pair \"%s\"", str); } return COLOR_PAIR(val); } static int get_char_attr(lua_State* L, int stack_pos) { int mode = A_NORMAL; lua_pushnil(L); while (lua_next(L, stack_pos) != 0) { if (lua_isstring(L, -2)) { const char* str; str = lua_tostring(L, -2); if (!strcmp(str, "color")) { mode |= get_char_color(L, stack_pos); } else { int cur_mode; cur_mode = get_mode_enum(str); if (cur_mode == -1) { return luaL_error(L, "Unknown attribute \"%s\"", str); } lua_toboolean(L, -1) ? (mode |= cur_mode) : (mode &= ~cur_mode); } } lua_pop(L, 1); } return mode; } NO_ARG_FUNCTION(initscr) NO_ARG_FUNCTION(endwin) NO_ARG_FUNCTION(erase) NO_ARG_FUNCTION(clear) NO_ARG_FUNCTION(clrtobot) NO_ARG_FUNCTION(clrtoeol) NO_ARG_FUNCTION(deleteln) NO_ARG_FUNCTION(insertln) NO_ARG_FUNCTION(refresh) NO_ARG_FUNCTION(beep) NO_ARG_FUNCTION(flash) static int l_isendwin(lua_State* L) { lua_pushboolean(L, isendwin()); return 1; } static int l_start_color(lua_State* L) { if (has_colors()) { init_color_pairs(L); init_colors(L); lua_pushboolean(L, start_color() == OK); } else { lua_pushboolean(L, FALSE); } return 1; } static int l_use_default_colors(lua_State* L) { register_default_color(L); lua_pushboolean(L, use_default_colors() == OK); return 1; } static int l_assume_default_colors(lua_State* L) { const char* fg; const char* bg; int nfg, nbg; register_default_color(L); fg = luaL_optlstring(L, 1, "default", NULL); bg = luaL_optlstring(L, 2, "default", NULL); lua_getfield(L, LUA_REGISTRYINDEX, REG_TABLE); lua_getfield(L, -1, "colors"); lua_getfield(L, -1, fg); nfg = luaL_checkint(L, -1); lua_pop(L, 1); lua_getfield(L, -1, bg); nbg = luaL_checkint(L, -1); lua_pop(L, 3); lua_pushboolean(L, assume_default_colors(nfg, nbg) == OK); return 1; } static int l_setup_term(lua_State* L) { int ret = 0; luaL_checktype(L, 1, LUA_TTABLE); lua_pushnil(L); while (lua_next(L, 1) != 0) { if (lua_isstring(L, -2)) { const char* str; str = lua_tostring(L, -2); if (!strcmp(str, "cbreak")) { ret += ((lua_toboolean(L, -1) ? cbreak() : nocbreak()) == OK); } else if (!strcmp(str, "echo")) { ret += ((lua_toboolean(L, -1) ? echo() : noecho()) == OK); } else if (!strcmp(str, "halfdelay")) { ret += (halfdelay(lua_tointeger(L, -1)) == OK); } else if (!strcmp(str, "intrflush")) { ret += (intrflush(stdscr, lua_toboolean(L, -1)) == OK); } else if (!strcmp(str, "keypad")) { ret += (keypad(stdscr, lua_toboolean(L, -1)) == OK); } else if (!strcmp(str, "meta")) { ret += (meta(stdscr, lua_toboolean(L, -1)) == OK); } else if (!strcmp(str, "nodelay")) { ret += (nodelay(stdscr, lua_toboolean(L, -1)) == OK); } else if (!strcmp(str, "raw")) { ret += ((lua_toboolean(L, -1) ? raw() : noraw()) == OK); } else if (!strcmp(str, "qiflush")) { lua_toboolean(L, -1) ? qiflush() : noqiflush(); ret++; } else if (!strcmp(str, "timeout")) { if (lua_isnil(L, -1)) { ret += (notimeout(stdscr, TRUE) == OK); } else { timeout(lua_tointeger(L, -1)); ret++; } } else if (!strcmp(str, "typeahead")) { ret += ((lua_toboolean(L, -1) ? typeahead(0) : typeahead(-1)) == OK); } else if (!strcmp(str, "clear")) { ret += (clearok(stdscr, lua_toboolean(L, -1)) == OK); } else if (!strcmp(str, "idl")) { ret += (idlok(stdscr, lua_toboolean(L, -1)) == OK); } else if (!strcmp(str, "idc")) { idcok(stdscr, lua_toboolean(L, -1)); ret++; } else if (!strcmp(str, "immed")) { immedok(stdscr, lua_toboolean(L, -1)); ret++; } else if (!strcmp(str, "leave")) { ret += (leaveok(stdscr, lua_toboolean(L, -1)) == OK); } else if (!strcmp(str, "scroll")) { ret += (scrollok(stdscr, lua_toboolean(L, -1)) == OK); } else if (!strcmp(str, "nl")) { ret += ((lua_toboolean(L, -1) ? nl() : nonl()) == OK); } else { luaL_error(L, "Unknown or unimplemented terminal mode %s", str); } } lua_pop(L, 1); } lua_pushnumber(L, ret); return 1; } static int l_init_color(lua_State* L) { const char* name; int color_val; short r, g, b; if (can_change_color() == FALSE) { lua_pushboolean(L, FALSE); return 1; } name = luaL_checklstring(L, 1, NULL); lua_getfield(L, LUA_REGISTRYINDEX, REG_TABLE); lua_getfield(L, -1, "colors"); lua_getfield(L, -1, name); if (lua_isnil(L, -1)) { lua_pop(L, 1); lua_pushinteger(L, ++ncolors); lua_pushvalue(L, -1); lua_setfield(L, -3, name); } color_val = lua_tointeger(L, -1); lua_pop(L, 2); if (lua_istable(L, 2)) { lua_getfield(L, 2, "r"); lua_getfield(L, 2, "g"); lua_getfield(L, 2, "b"); r = luaL_checkint(L, -3) * 1000 / 256; g = luaL_checkint(L, -2) * 1000 / 256; b = luaL_checkint(L, -1) * 1000 / 256; lua_pop(L, 3); } else { r = luaL_checkint(L, 2) * 1000 / 256; g = luaL_checkint(L, 3) * 1000 / 256; b = luaL_checkint(L, 4) * 1000 / 256; } lua_pushboolean(L, init_color(color_val, r, g, b) == OK); return 1; } static int l_init_pair(lua_State* L) { const char *name, *fg, *bg; int name_val, fg_val, bg_val; /* check the arguments, and get them */ name = luaL_checklstring(L, 1, NULL); if (default_color_available) { fg = luaL_optlstring(L, 2, "default", NULL); bg = luaL_optlstring(L, 3, "default", NULL); } else { fg = luaL_optlstring(L, 2, "white", NULL); bg = luaL_optlstring(L, 3, "black", NULL); } lua_getfield(L, LUA_REGISTRYINDEX, REG_TABLE); /* figure out which pair value to use */ lua_getfield(L, -1, "color_pairs"); lua_getfield(L, -1, name); if (lua_isnil(L, -1)) { /* if it was nil, we want to set a new value in the color_pairs table, * and we want to leave that C color_pair value on top of the stack * for consistency */ lua_pop(L, 1); lua_pushinteger(L, ++ncolor_pairs); lua_pushvalue(L, -1); lua_setfield(L, -3, name); } name_val = lua_tointeger(L, -1); lua_pop(L, 2); /* figure out which foreground value to use */ lua_getfield(L, -1, "colors"); lua_getfield(L, -1, fg); if (lua_isnil(L, -1)) { return luaL_error(L, "init_pair: Trying to use a non-existant foreground color: \"%s\"", fg); } fg_val = lua_tointeger(L, -1); lua_pop(L, 1); /* and background value */ lua_getfield(L, -1, bg); if (lua_isnil(L, -1)) { return luaL_error(L, "init_pair: Trying to use a non-existant background color: \"%s\"", bg); } bg_val = lua_tointeger(L, -1); lua_pop(L, 3); if (name_val != 0) { lua_pushboolean(L, (init_pair(name_val, fg_val, bg_val) == OK)); } else { lua_pushboolean(L, (assume_default_colors(fg_val, bg_val) == OK)); } return 1; } static int l_getch(lua_State* L) { int c; pos p; const char* key_name; if (get_pos(L, &p)) { c = mvgetch(p.y, p.x); } else { c = getch(); } if (c == ERR) { lua_pushboolean(L, 0); return 1; } key_name = get_key_str(c); if (key_name == NULL) { char s; s = c; lua_pushlstring(L, &s, 1); } else { lua_pushstring(L, key_name); } return 1; } static int l_ungetch(lua_State* L) { const char* ch_str; int ch; ch_str = luaL_checklstring(L, 1, NULL); ch = get_key_enum(ch_str); lua_pushboolean(L, ungetch(ch) == OK); return 1; } static int l_move(lua_State* L) { pos p; if (get_pos(L, &p)) { lua_pushboolean(L, (move(p.y, p.x) == OK)); } else { int x, y; y = luaL_checkint(L, 1); x = luaL_checkint(L, 2); lua_pushboolean(L, (move(y, x) == OK)); } return 1; } static int l_addch(lua_State* L) { int is_mv; pos p; chtype ch; is_mv = get_pos(L, &p); ch = get_char_enum(luaL_checklstring(L, 1, NULL)); if (lua_istable(L, 2)) { ch |= get_char_attr(L, 2); } if (is_mv) { lua_pushboolean(L, mvaddch(p.y, p.x, ch) == OK); } else { lua_pushboolean(L, addch(ch) == OK); } return 1; } static int l_echochar(lua_State* L) { int is_mv; pos p; chtype ch; is_mv = get_pos(L, &p); ch = get_char_enum(luaL_checklstring(L, 1, NULL)); if (lua_istable(L, 2)) { ch |= get_char_attr(L, 2); } if (is_mv) { int ret; ret = move(p.y, p.x); if (ret == OK) { ret = echochar(ch); } lua_pushboolean(L, ret == OK); } else { lua_pushboolean(L, echochar(ch) == OK); } return 1; } static int l_addstr(lua_State* L) { int is_mv, set_attrs = 0; pos p; const char* str; attr_t old_mode = 0; short old_color = 0; is_mv = get_pos(L, &p); str = luaL_checklstring(L, 1, NULL); if (lua_istable(L, 2)) { int new_mode, new_color; set_attrs = 1; attr_get(&old_mode, &old_color, NULL); new_mode = get_char_attr(L, 2); new_color = PAIR_NUMBER(new_mode); new_mode &= A_ATTRIBUTES & ~A_COLOR; attr_set(new_mode, new_color, NULL); } if (is_mv) { lua_pushboolean(L, mvaddstr(p.y, p.x, str) == OK); } else { lua_pushboolean(L, addstr(str) == OK); } if (set_attrs) { attr_set(old_mode, old_color, NULL); } return 1; } static int l_delch(lua_State* L) { pos p; if (get_pos(L, &p)) { lua_pushboolean(L, mvdelch(p.y, p.x) == OK); } else { lua_pushboolean(L, delch() == OK); } return 1; } static int l_insch(lua_State* L) { int is_mv; pos p; chtype ch; is_mv = get_pos(L, &p); ch = get_char_enum(luaL_checklstring(L, 1, NULL)); if (lua_istable(L, 2)) { ch |= get_char_attr(L, 2); } if (is_mv) { lua_pushboolean(L, mvinsch(p.y, p.x, ch) == OK); } else { lua_pushboolean(L, insch(ch) == OK); } return 1; } static int l_insstr(lua_State* L) { int is_mv, set_attrs = 0; pos p; const char* str; attr_t old_mode = 0; short old_color = 0; is_mv = get_pos(L, &p); str = luaL_checklstring(L, 1, NULL); if (lua_istable(L, 2)) { int new_mode, new_color; set_attrs = 1; attr_get(&old_mode, &old_color, NULL); new_mode = get_char_attr(L, 2); new_color = PAIR_NUMBER(new_mode); new_mode &= A_ATTRIBUTES & ~A_COLOR; attr_set(new_mode, new_color, NULL); } if (is_mv) { lua_pushboolean(L, mvinsstr(p.y, p.x, str) == OK); } else { lua_pushboolean(L, insstr(str) == OK); } if (set_attrs) { attr_set(old_mode, old_color, NULL); } return 1; } static int l_insdelln(lua_State* L) { int n; n = luaL_checkint(L, 1); lua_pushboolean(L, (insdelln(n) == OK)); return 1; } static int l_getmaxyx(lua_State* L) { int x, y; getmaxyx(stdscr, y, x); lua_pushnumber(L, y); lua_pushnumber(L, x); return 2; } static int l_getyx(lua_State* L) { int x, y; getyx(stdscr, y, x); lua_pushnumber(L, y); lua_pushnumber(L, x); return 2; } static int l_colors(lua_State* L) { lua_pushinteger(L, COLORS); return 1; } static int l_color_pairs(lua_State* L) { lua_pushinteger(L, COLOR_PAIRS); return 1; } const luaL_Reg reg[] = { { "initscr", l_initscr }, { "endwin", l_endwin }, { "isendwin", l_isendwin }, { "start_color", l_start_color }, { "use_default_colors", l_use_default_colors }, { "assume_default_colors", l_assume_default_colors }, { "setup_term", l_setup_term }, { "init_color", l_init_color }, { "init_pair", l_init_pair }, { "getch", l_getch }, { "ungetch", l_ungetch }, { "move", l_move }, { "addch", l_addch }, { "echochar", l_echochar }, { "addstr", l_addstr }, { "erase", l_erase }, { "clear", l_clear }, { "clrtobot", l_clrtobot }, { "clrtoeol", l_clrtoeol }, { "delch", l_delch }, { "deleteln", l_deleteln }, { "insch", l_insch }, { "insstr", l_insstr }, { "insdelln", l_insdelln }, { "insertln", l_insertln }, { "refresh", l_refresh }, { "getmaxyx", l_getmaxyx }, { "getyx", l_getyx }, { "colors", l_colors }, { "color_pairs", l_color_pairs }, { "beep", l_beep }, { "flash", l_flash }, { NULL, NULL }, }; extern int luaopen_curses(lua_State* L) { lua_newtable(L); lua_setfield(L, LUA_REGISTRYINDEX, REG_TABLE); luaL_register(L, "curses", reg); lua_getglobal(L, "curses"); lua_pushstring(L, "LuaNcurses 0.02"); lua_setfield(L, -2, "_VERSION"); return 1; }