#include "LuaEngine.h" #include #include #include #include "SDL2/include/SDL.h" #include "SDL2/include/SDL_mixer.h" #include "SDL2/include/SDL_ttf.h" #include "SDL2/include/SDL_image.h" using namespace std; void check(lua_State* L, const vector& typearr) { int sz = typearr.size(); for (int i = 0; i < sz; i++) { if (lua_type(L, i + 1) != typearr[i]) { char tmp[128] = { 0 }; sprintf(tmp, "Bad argument #%d. %s expected, got %s", i + 1, lua_typename(L, typearr[i]), lua_typename(L, lua_type(L, i + 1))); luaL_error(L, tmp); } } } struct LuaTimerCallbackData { void* p; int id; int type; // 0=setTimeout, 1=setInterval }; Uint32 LuaTimerCallbackGate(Uint32 interval, void* param) { SDL_Event e; e.type = SDL_USEREVENT; e.user.code = 1001; e.user.data1 = param; SDL_PushEvent(&e); return ((LuaTimerCallbackData*)(param))->type ? interval : 0; } #define setfn(L, cfunc, name) lua_pushcfunction(L, cfunc); lua_setfield(L, -2, name) struct Window { SDL_Window* wnd; static int close(lua_State* L) { check(L, { LUA_TUSERDATA }); Window* p = (Window*)lua_touserdata(L, 1); if (p->wnd) { // release data table lua_pushlightuserdata(L, p); lua_pushnil(L); lua_settable(L, LUA_REGISTRYINDEX); SDL_DestroyWindow(p->wnd); p->wnd = nullptr; } return 0; } static int start(lua_State* L) { check(L, { LUA_TUSERDATA }); Window* p = (Window*)lua_touserdata(L, 1); lua_settop(L, 1); lua_pushlightuserdata(L, p); lua_gettable(L, LUA_REGISTRYINDEX); bool running = true; SDL_Event e; while (running && SDL_WaitEvent(&e)) { switch (e.type) { case SDL_MOUSEBUTTONDOWN: { if (lua_getfield(L, -1, "onclick") != LUA_TNIL) { lua_pushnumber(L, e.button.x); lua_pushnumber(L, e.button.y); lua_call(L, 2, LUA_MULTRET); } else lua_pop(L, 1); break; } case SDL_QUIT: { if (lua_getfield(L, -1, "onquit") != LUA_TNIL) { lua_call(L, 0, LUA_MULTRET); } else lua_pop(L, 1); running = 0; break; } case SDL_KEYDOWN: { if (lua_getfield(L, -1, "onkeydown") != LUA_TNIL) { lua_pushnumber(L, e.key.keysym.sym); lua_call(L, 1, LUA_MULTRET); } else lua_pop(L, 1); break; } case SDL_USEREVENT: { switch (e.user.code) { case 1001: { LuaTimerCallbackData* pd = (LuaTimerCallbackData* )e.user.data1; lua_pushlightuserdata(L, pd->p); lua_gettable(L, LUA_REGISTRYINDEX); lua_pushfstring(L, "tmcb%d", pd->id); lua_gettable(L, -2); delete pd; lua_call(L, 0, LUA_MULTRET); lua_pop(L, 1); break; } } break; } } } return 0; } static int set_event_handler(lua_State* L) { check(L, { LUA_TUSERDATA, LUA_TSTRING, LUA_TFUNCTION }); Window* p = (Window*)lua_touserdata(L, 1); lua_pushlightuserdata(L, p); lua_gettable(L, LUA_REGISTRYINDEX); lua_pushfstring(L, "on%s", lua_tostring(L, 2)); lua_pushvalue(L, 3); lua_settable(L, -3); return 0; } static int setTimeout(lua_State* L) { check(L, { LUA_TUSERDATA, LUA_TFUNCTION, LUA_TNUMBER }); // ms Window* p = (Window*)lua_touserdata(L, 1); LuaTimerCallbackData* pd = new LuaTimerCallbackData(); pd->p = p; pd->type = 0; pd->id = SDL_AddTimer(lua_tointeger(L, 3), LuaTimerCallbackGate, pd); lua_pushlightuserdata(L, p); lua_gettable(L, LUA_REGISTRYINDEX); lua_pushfstring(L, "tmcb%d", pd->id); lua_pushvalue(L, 2); lua_settable(L, -3); return 0; } static int create(lua_State* L) { check(L, { LUA_TSTRING, LUA_TNUMBER, LUA_TNUMBER }); cout << "In SDLWindow::Create" << endl; Window* p = (Window*) lua_newuserdata(L, sizeof(Window)); // Data table (for storing lua values) lua_pushlightuserdata(L, p); lua_newtable(L); lua_settable(L, LUA_REGISTRYINDEX); // Metatable lua_getfield(L, LUA_REGISTRYINDEX, "__window_mt"); if (lua_type(L, -1) == LUA_TNIL) { lua_pop(L, 1); lua_newtable(L); // GC lua_pushcfunction(L, close); lua_setfield(L, -2, "__gc"); // Fields lua_newtable(L); lua_pushstring(L, "window"); lua_setfield(L, -2, "type"); setfn(L, close, "close"); setfn(L, set_event_handler, "on"); setfn(L, start, "start"); setfn(L, setTimeout, "setTimeout"); // Binding lua_setfield(L, -2, "__index"); lua_setfield(L, LUA_REGISTRYINDEX, "__window_mt"); lua_getfield(L, LUA_REGISTRYINDEX, "__window_mt"); } lua_setmetatable(L, -2); // Internal logic p->wnd = SDL_CreateWindow(lua_tostring(L, 1), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, lua_tointeger(L, 2), lua_tointeger(L, 3), SDL_WINDOW_SHOWN); return 1; } }; struct Renderer { SDL_Renderer* rnd; static int close(lua_State* L) { check(L, { LUA_TUSERDATA }); Renderer* p = (Renderer*)lua_touserdata(L, 1); if (p->rnd) { SDL_DestroyRenderer(p->rnd); p->rnd = nullptr; } return 0; } static int update(lua_State* L) { check(L, { LUA_TUSERDATA }); Renderer* p = (Renderer*)lua_touserdata(L, 1); SDL_RenderPresent(p->rnd); return 0; } static int clear(lua_State* L) { check(L, { LUA_TUSERDATA }); Renderer* p = (Renderer*)lua_touserdata(L, 1); SDL_RenderClear(p->rnd); return 0; } static int create(lua_State* L) { check(L, { LUA_TUSERDATA }); cout << "In SDLWindow::Create" << endl; Renderer* p = (Renderer*)lua_newuserdata(L, sizeof(Renderer)); lua_getfield(L, LUA_REGISTRYINDEX, "__renderer_mt"); if (lua_type(L, -1) == LUA_TNIL) { lua_pop(L, 1); lua_newtable(L); // GC lua_pushcfunction(L, close); lua_setfield(L, -2, "__gc"); // Fields lua_newtable(L); lua_pushstring(L, "renderer"); lua_setfield(L, -2, "type"); setfn(L, close, "close"); setfn(L, update, "update"); setfn(L, clear, "clear"); // Set __index of metatable. lua_setfield(L, -2, "__index"); lua_setfield(L, LUA_REGISTRYINDEX, "__renderer_mt"); lua_getfield(L, LUA_REGISTRYINDEX, "__renderer_mt"); } lua_setmetatable(L, -2); // Internal logic p->rnd = SDL_CreateRenderer(static_cast(lua_touserdata(L, 1))->wnd, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE); return 1; } }; #define reg_in_lua(L, cfunc, name) lua_pushcfunction(L, cfunc); lua_setglobal(L, name) int InitEngine(lua_State* L) { SDL_Init(SDL_INIT_EVERYTHING); IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG); TTF_Init(); Mix_Init(MIX_INIT_MP3); lua_newtable(L); reg_in_lua(L, Window::create, "Window"); reg_in_lua(L, Renderer::create, "Renderer"); return 0; }