288 lines
6.4 KiB
C++
288 lines
6.4 KiB
C++
|
#include "LuaEngine.h"
|
||
|
#include <iostream>
|
||
|
#include <string>
|
||
|
#include <vector>
|
||
|
#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<int>& 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<Window*>(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;
|
||
|
}
|