#ifndef _XOPEN_SOURCE_EXTENDED #define _XOPEN_SOURCE_EXTENDED #endif #include #include #include #include #include #include #include #include #include #include #include extern const char main_luac[]; extern const unsigned int main_luac_len; static lua_State *G_L = NULL; static volatile int g_resize = 0; static void on_sigwinch(int sig) { (void)sig; g_resize = 1; } static int push_error(lua_State *L, const char *msg) { lua_pushnil(L); lua_pushstring(L, msg); return 2; } static int l_init_ncurses(lua_State *L) { (void)L; setlocale(LC_ALL, ""); initscr(); cbreak(); raw(); noecho(); keypad(stdscr, TRUE); nodelay(stdscr, FALSE); curs_set(0); start_color(); use_default_colors(); init_pair(1, COLOR_BLACK, COLOR_WHITE); signal(SIGWINCH, on_sigwinch); return 0; } static int l_end_ncurses(lua_State *L) { (void)L; endwin(); return 0; } static int l_resize_terminal(lua_State *L) { (void)L; if (g_resize) { g_resize = 0; endwin(); refresh(); clearok(stdscr, TRUE); } return 0; } static int l_refresh_screen(lua_State *L) { (void)L; wrefresh(stdscr); return 0; } static int l_get_wchar(lua_State *L) { wint_t ch; int res = wget_wch(stdscr, &ch); if (res == ERR) lua_pushinteger(L, -1); else lua_pushinteger(L, (lua_Integer)ch); return 1; } static int l_set_title(lua_State *L) { const char *title = luaL_checkstring(L, 1); printf("\033]0;%s\007", title); fflush(stdout); return 0; } static int l_get_screen_size(lua_State *L) { int rows, cols; getmaxyx(stdscr, rows, cols); lua_pushinteger(L, rows); lua_pushinteger(L, cols + 1); return 2; } static int l_clear_screen(lua_State *L) { (void)L; clear(); return 0; } static int l_move_cursor(lua_State *L) { int y = (int)luaL_checkinteger(L, 1); int x = (int)luaL_checkinteger(L, 2); move(y, x); return 0; } static int l_print_at(lua_State *L) { int y = (int)luaL_checkinteger(L, 1); int x = (int)luaL_checkinteger(L, 2); const char *str = luaL_checkstring(L, 3); mvprintw(y, x, "%s", str); return 0; } static int l_set_reverse(lua_State *L) { if (lua_toboolean(L, 1)) attron(A_REVERSE); else attroff(A_REVERSE); return 0; } static int l_set_bold(lua_State *L) { if (lua_toboolean(L, 1)) attron(A_BOLD); else attroff(A_BOLD); return 0; } static int l_set_color(lua_State *L) { int pair = (int)luaL_checkinteger(L, 1); int enable = lua_toboolean(L, 2); if (enable) attron(COLOR_PAIR(pair)); else attroff(COLOR_PAIR(pair)); return 0; } static int l_init_color_pair(lua_State *L) { int pair = (int)luaL_checkinteger(L, 1); int fg = (int)luaL_checkinteger(L, 2); int bg = (int)luaL_checkinteger(L, 3); init_pair((short)pair, (short)fg, (short)bg); return 0; } static int l_save_file(lua_State *L) { const char *fname = luaL_checkstring(L, 1); const char *content = luaL_checkstring(L, 2); FILE *f = fopen(fname, "w"); if (!f) return push_error(L, "Open failed"); size_t len = strlen(content); if (len > 0 && fwrite(content, 1, len, f) != len) { fclose(f); return push_error(L, "Write failed"); } fclose(f); lua_pushboolean(L, 1); return 1; } static int l_load_file(lua_State *L) { const char *fname = luaL_checkstring(L, 1); FILE *f = fopen(fname, "r"); if (!f) return push_error(L, "Open failed"); fseek(f, 0, SEEK_END); long size = ftell(f); fseek(f, 0, SEEK_SET); if (size < 0) { fclose(f); return push_error(L, "Seek failed"); } char *buf = malloc((size_t)size + 1); if (!buf) { fclose(f); return push_error(L, "Alloc failed"); } size_t r = fread(buf, 1, (size_t)size, f); buf[r] = '\0'; fclose(f); lua_pushlstring(L, buf, r); free(buf); return 1; } static int l_list_dir(lua_State *L) { const char *path = luaL_checkstring(L, 1); DIR *d = opendir(path); if (!d) { lua_pushnil(L); return 1; } lua_newtable(L); int idx = 1; struct dirent *ent; while ((ent = readdir(d)) != NULL) { lua_pushstring(L, ent->d_name); lua_rawseti(L, -2, idx++); } closedir(d); return 1; } static int l_stat_file(lua_State *L) { const char *path = luaL_checkstring(L, 1); struct stat st; if (stat(path, &st) != 0) { lua_pushnil(L); return 1; } if (S_ISREG(st.st_mode)) lua_pushstring(L, "file"); else if (S_ISDIR(st.st_mode)) lua_pushstring(L, "dir"); else lua_pushstring(L, "other"); return 1; } static int l_lua_eval(lua_State *L) { const char *code = luaL_checkstring(L, 1); int top_before = lua_gettop(G_L); if (luaL_loadstring(G_L, code) != LUA_OK) { const char *err = lua_tostring(G_L, -1); lua_pop(G_L, 1); lua_pushstring(L, err ? err : "compile error"); return 1; } if (lua_pcall(G_L, 0, LUA_MULTRET, 0) != LUA_OK) { const char *err = lua_tostring(G_L, -1); lua_pop(G_L, 1); lua_pushstring(L, err ? err : "runtime error"); return 1; } int nret = lua_gettop(G_L) - top_before; if (nret > 0) { const char *res = lua_tostring(G_L, -1); lua_pushstring(L, res ? res : "(non-string result)"); lua_settop(G_L, top_before); } else { lua_pushstring(L, "ok"); } return 1; } static const struct { const char *name; lua_CFunction func; } bindings[] = { {"init_ncurses", l_init_ncurses}, {"end_ncurses", l_end_ncurses}, {"resize_terminal", l_resize_terminal}, {"refresh_screen", l_refresh_screen}, {"get_wchar", l_get_wchar}, {"set_title", l_set_title}, {"get_screen_size", l_get_screen_size}, {"clear_screen", l_clear_screen}, {"move_cursor", l_move_cursor}, {"print_at", l_print_at}, {"set_reverse", l_set_reverse}, {"set_bold", l_set_bold}, {"set_color", l_set_color}, {"init_color_pair", l_init_color_pair}, {"save_file", l_save_file}, {"load_file", l_load_file}, {"list_dir", l_list_dir}, {"stat_file", l_stat_file}, {"lua_eval", l_lua_eval}, {NULL, NULL} }; int main(int argc, char *argv[]) { lua_State *L = luaL_newstate(); if (!L) { fprintf(stderr, "Lua init failed\n"); return 1; } G_L = L; luaL_openlibs(L); for (int i = 0; bindings[i].name; i++) { lua_pushcfunction(L, bindings[i].func); lua_setglobal(L, bindings[i].name); } lua_newtable(L); for (int i = 1; i < argc; i++) { lua_pushstring(L, argv[i]); lua_rawseti(L, -2, i); } lua_setglobal(L, "arg"); if (luaL_loadbuffer(L, main_luac, main_luac_len, "main.lua") || lua_pcall(L, 0, 0, 0)) { fprintf(stderr, "Lume: %s\n", lua_tostring(L, -1)); lua_pop(L, 1); lua_close(L); return 1; } lua_close(L); return 0; }