1635 lines
39 KiB
C++
1635 lines
39 KiB
C++
#include "LuaEngine.h"
|
||
#include <iostream>
|
||
#include <string>
|
||
#include <thread>
|
||
#include <vector>
|
||
#include <cstdlib>
|
||
#include "SDL2/include/SDL.h"
|
||
#include "SDL2/include/SDL_mixer.h"
|
||
#include "SDL2/include/SDL_ttf.h"
|
||
#include "SDL2/include/SDL_image.h"
|
||
#include "PlatAPI.h"
|
||
#include "LuaEnum.h"
|
||
#include "LuaCommon.h"
|
||
#include "LuaNetwork.h"
|
||
using namespace std;
|
||
|
||
// Global single-thread unprotected event callback id counter
|
||
int callback_counter = 1;
|
||
|
||
// Generate SDL error on lua stack.
|
||
// NOTE: Never returns. If this function is used in C++ context, you should compile Lua as C++.
|
||
void LuaSDLError(lua_State* L)
|
||
{
|
||
luaL_error(L, "SDLError: %s", SDL_GetError());
|
||
}
|
||
|
||
void ConvertMusicAsyncImpl(const string& filename, const string& targetname, int ticket)
|
||
{
|
||
string command = "bin\\ffmpeg.exe -y -loglevel error -hide_banner -i \""s + filename + "\" \"" + targetname + "\"";
|
||
cout << command << endl;
|
||
system(command.c_str());
|
||
SDL_Event e;
|
||
e.type = SDL_USEREVENT;
|
||
e.user.code = 1002; // General zero ret callback.
|
||
e.user.data1 = new int(ticket);
|
||
e.user.data2 = nullptr;
|
||
SDL_PushEvent(&e);
|
||
}
|
||
|
||
static int ConvertMusicAsync(lua_State* L)
|
||
{
|
||
check(L, { LUA_TSTRING, LUA_TSTRING, LUA_TFUNCTION });
|
||
lua_pushfstring(L, "cb%d", callback_counter);
|
||
lua_pushvalue(L, 3);
|
||
lua_settable(L, LUA_REGISTRYINDEX);
|
||
string filename(lua_tostring(L, 1));
|
||
string targetname(lua_tostring(L, 2));
|
||
thread td(ConvertMusicAsyncImpl, filename, targetname, callback_counter);
|
||
td.detach();
|
||
lua_pushnumber(L, callback_counter);
|
||
callback_counter++;
|
||
return 1;
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
int GetMouseState(const char** output, Uint32 state)
|
||
{
|
||
int i = 0;
|
||
if (state & SDL_BUTTON_LMASK)
|
||
{
|
||
output[i++] = "left";
|
||
}
|
||
if (state & SDL_BUTTON_MMASK)
|
||
{
|
||
output[i++] = "middle";
|
||
}
|
||
if (state & SDL_BUTTON_RMASK)
|
||
{
|
||
output[i++] = "right";
|
||
}
|
||
if (state & SDL_BUTTON_X1MASK)
|
||
{
|
||
output[i++] = "x1";
|
||
}
|
||
if (state & SDL_BUTTON_X2MASK)
|
||
{
|
||
output[i++] = "x2";
|
||
}
|
||
return i;
|
||
}
|
||
|
||
int LuaColorToColor(lua_State* L, int index, SDL_Color& c)
|
||
{
|
||
if (lua_getfield(L, index, "type") != LUA_TSTRING)
|
||
{
|
||
lua_pop(L, 1);
|
||
return -1;
|
||
}
|
||
|
||
const char* type = lua_tostring(L, -1);
|
||
if (strcmp(type, "rgba") == 0)
|
||
{
|
||
lua_pop(L, 1);
|
||
lua_getfield(L, index, "r");
|
||
c.r = lua_tointeger(L, -1);
|
||
lua_pop(L, 1);
|
||
lua_getfield(L, index, "g");
|
||
c.g = lua_tointeger(L, -1);
|
||
lua_pop(L, 1);
|
||
lua_getfield(L, index, "b");
|
||
c.b = lua_tointeger(L, -1);
|
||
lua_pop(L, 1);
|
||
lua_getfield(L, index, "a");
|
||
c.a = lua_tointeger(L, -1);
|
||
lua_pop(L, 1);
|
||
|
||
return 0;
|
||
}
|
||
else
|
||
{
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
#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 });
|
||
cout << "In Window::close" << endl;
|
||
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 set_event_handler(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA, LUA_TSTRING, LUA_TFUNCTION }, { "window" });
|
||
if (strcmp(lua_tostring(L, 2), "quit") == 0) // quit<69>¼<EFBFBD><C2BC><EFBFBD><EFBFBD><EFBFBD><E2B4A6>
|
||
{
|
||
lua_pushlightuserdata(L, &StartEngine);
|
||
lua_gettable(L, LUA_REGISTRYINDEX);
|
||
lua_getfield(L, -1, "onquit");
|
||
lua_pushvalue(L, 3);
|
||
lua_setfield(L, -3, "onquit");
|
||
return 1; // return old callback.
|
||
}
|
||
|
||
Window* p = (Window*)lua_touserdata(L, 1);
|
||
if (p->wnd)
|
||
{
|
||
lua_pushlightuserdata(L, p->wnd);
|
||
if (lua_gettable(L, LUA_REGISTRYINDEX) != LUA_TTABLE)
|
||
{
|
||
lua_pop(L, 1);
|
||
|
||
lua_pushlightuserdata(L, p->wnd);
|
||
|
||
lua_newtable(L);
|
||
lua_pushfstring(L, "on%s", lua_tostring(L, 2));
|
||
lua_pushvalue(L, 3);
|
||
lua_settable(L, -3);
|
||
|
||
lua_settable(L, LUA_REGISTRYINDEX);
|
||
|
||
lua_pushnil(L); // No old callback. Will return empty.
|
||
}
|
||
else
|
||
{
|
||
lua_pushfstring(L, "on%s", lua_tostring(L, 2));
|
||
lua_pushvalue(L, -1);
|
||
// window, eventName, callback, windowCBTable, onEvent, onEvent
|
||
lua_gettable(L, -3);
|
||
// window, eventName, callback, windowCBTable, onEvent, oldCallback
|
||
lua_pushvalue(L, -2);
|
||
lua_pushvalue(L, 3);
|
||
// window, eventName, callback, windowCBTable, onEvent, oldCallback, onEvent, callback
|
||
lua_settable(L, -5);
|
||
// window, eventName, callback, windowCBTable, onEvent, oldCallback
|
||
}
|
||
return 1;
|
||
}
|
||
else
|
||
{
|
||
return luaL_error(L, "Unable to add event listener to null window.");
|
||
}
|
||
}
|
||
|
||
static int showWindow(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA });
|
||
Window* p = (Window*)lua_touserdata(L, 1);
|
||
SDL_ShowWindow(p->wnd);
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int hideWindow(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA });
|
||
Window* p = (Window*)lua_touserdata(L, 1);
|
||
|
||
SDL_HideWindow(p->wnd);
|
||
return 0;
|
||
}
|
||
|
||
static int create(lua_State* L)
|
||
{
|
||
check(L, { LUA_TSTRING, LUA_TNUMBER, LUA_TNUMBER });
|
||
cout << "In Window::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, showWindow, "show");
|
||
setfn(L, hideWindow, "hide");
|
||
|
||
// 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_HIDDEN);
|
||
return 1;
|
||
}
|
||
};
|
||
|
||
|
||
struct Texture
|
||
{
|
||
SDL_Texture* text;
|
||
int w;
|
||
int h;
|
||
|
||
static int close(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA });
|
||
cout << "In Texture::close" << endl;
|
||
Texture* t = (Texture*)lua_touserdata(L, 1);
|
||
if (t->text)
|
||
{
|
||
SDL_DestroyTexture(t->text);
|
||
t->text = nullptr;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static void create(lua_State* L) // -2, +1
|
||
{
|
||
// <20>˺<EFBFBD><CBBA><EFBFBD><EFBFBD><EFBFBD>Renderer<65><72>C<EFBFBD><43>ֱ<EFBFBD>ӵ<EFBFBD><D3B5><EFBFBD>. <20><><EFBFBD><EFBFBD>ǰ<EFBFBD><C7B0>Ҫ<EFBFBD>ȷ<EFBFBD>һ<EFBFBD><D2BB>renderer userdata<74><61>lightuserdata<74><61>ջ<EFBFBD><D5BB>, ֵ<><D6B5><EFBFBD>ǵ<EFBFBD>ǰ<EFBFBD><C7B0>SDL_Texture.
|
||
// <20><><EFBFBD>ú<EFBFBD>ջ<EFBFBD>ϻ<EFBFBD><CFBB><EFBFBD><EFBFBD><EFBFBD>Texture<72><65>userdata.
|
||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Lua<75><61>û<EFBFBD><C3BB>ֱ<EFBFBD>Ӵ<EFBFBD><D3B4><EFBFBD>Texture<72>ķ<EFBFBD><C4B7><EFBFBD>.
|
||
cout << "In Texture::create" << endl;
|
||
Texture* p = (Texture*)lua_newuserdata(L, sizeof(Texture));
|
||
// Metatable
|
||
lua_getfield(L, LUA_REGISTRYINDEX, "__texture_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, "texture"); lua_setfield(L, -2, "type");
|
||
setfn(L, close, "close");
|
||
|
||
// Binding
|
||
lua_setfield(L, -2, "__index");
|
||
|
||
lua_setfield(L, LUA_REGISTRYINDEX, "__texture_mt");
|
||
lua_getfield(L, LUA_REGISTRYINDEX, "__texture_mt");
|
||
}
|
||
lua_setmetatable(L, -2);
|
||
p->text = (SDL_Texture * )lua_touserdata(L, -2);
|
||
SDL_QueryTexture(p->text, NULL, NULL, &p->w, &p->h);
|
||
lua_pushvalue(L, -3);
|
||
lua_setuservalue(L, -2); // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Rendere userdata
|
||
|
||
// stack balance
|
||
lua_remove(L, lua_gettop(L) - 1); // ɾ<><C9BE>lightuserdata
|
||
lua_remove(L, lua_gettop(L) - 1); // ɾ<><C9BE>rendere userdata
|
||
}
|
||
};
|
||
|
||
int LuaRectPointToRect(lua_State* L, int index, SDL_Rect& rect)
|
||
{
|
||
if (lua_getfield(L, index, "type") != LUA_TSTRING)
|
||
{
|
||
lua_pop(L, 1);
|
||
return -1;
|
||
}
|
||
|
||
const char* type = lua_tostring(L, -1);
|
||
if (strcmp(type, "point") == 0)
|
||
{
|
||
lua_pop(L, 1);
|
||
lua_getfield(L, index, "x");
|
||
rect.x = lua_tointeger(L, -1);
|
||
lua_pop(L, 1);
|
||
lua_getfield(L, index, "y");
|
||
rect.y = lua_tointeger(L, -1);
|
||
lua_pop(L, 1);
|
||
|
||
return 1;
|
||
}
|
||
else if (strcmp(type, "rect") == 0)
|
||
{
|
||
lua_pop(L, 1);
|
||
lua_getfield(L, index, "x");
|
||
rect.x = lua_tointeger(L, -1);
|
||
lua_pop(L, 1);
|
||
lua_getfield(L, index, "y");
|
||
rect.y = lua_tointeger(L, -1);
|
||
lua_pop(L, 1);
|
||
lua_getfield(L, index, "w");
|
||
rect.w = lua_tointeger(L, -1);
|
||
lua_pop(L, 1);
|
||
lua_getfield(L, index, "h");
|
||
rect.h = lua_tointeger(L, -1);
|
||
lua_pop(L, 1);
|
||
|
||
return 0;
|
||
}
|
||
else
|
||
{
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
struct Surface
|
||
{
|
||
SDL_Surface* surf;
|
||
|
||
static int close(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA });
|
||
cout << "In Surface::close" << endl;
|
||
Surface* p = (Surface*)lua_touserdata(L, 1);
|
||
if (p->surf)
|
||
{
|
||
SDL_FreeSurface(p->surf);
|
||
p->surf = nullptr;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static void create(lua_State* L) // -1, +1
|
||
{
|
||
cout << "In Surface::create" << endl;
|
||
Surface* p = (Surface*)lua_newuserdata(L, sizeof(Surface));
|
||
// Metatable
|
||
lua_getfield(L, LUA_REGISTRYINDEX, "__surface_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, "surface"); lua_setfield(L, -2, "type");
|
||
setfn(L, close, "close");
|
||
|
||
// Binding
|
||
lua_setfield(L, -2, "__index");
|
||
|
||
lua_setfield(L, LUA_REGISTRYINDEX, "__surface_mt");
|
||
lua_getfield(L, LUA_REGISTRYINDEX, "__surface_mt");
|
||
}
|
||
lua_setmetatable(L, -2);
|
||
p->surf = (SDL_Surface*)lua_touserdata(L, -2);
|
||
|
||
// stack balance
|
||
lua_remove(L, lua_gettop(L) - 1); // ɾ<><C9BE>lightuserdata
|
||
}
|
||
};
|
||
|
||
struct Renderer
|
||
{
|
||
SDL_Renderer* rnd;
|
||
|
||
static int close(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA });
|
||
cout << "In Renderer::close" << endl;
|
||
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 setColor(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA, LUA_TTABLE });
|
||
Renderer* p = (Renderer*)lua_touserdata(L, 1);
|
||
SDL_Color c;
|
||
if (LuaColorToColor(L, 2, c) < 0)
|
||
{
|
||
luaL_error(L, "Bad argument #%d. Color expected, got %s", 2, lua_typename(L, lua_type(L, 2)));
|
||
}
|
||
SDL_SetRenderDrawColor(p->rnd, c.r, c.g, c.b, c.a);
|
||
return 0;
|
||
}
|
||
|
||
static int getColor(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA });
|
||
Renderer* p = (Renderer*)lua_touserdata(L, 1);
|
||
Uint8 r, b, g, a;
|
||
SDL_GetRenderDrawColor(p->rnd, &r, &g, &b, &a);
|
||
lua_newtable(L);
|
||
lua_pushinteger(L, r);
|
||
lua_setfield(L, -2, "r");
|
||
lua_pushinteger(L, g);
|
||
lua_setfield(L, -2, "g");
|
||
lua_pushinteger(L, b);
|
||
lua_setfield(L, -2, "b");
|
||
lua_pushinteger(L, a);
|
||
lua_setfield(L, -2, "a");
|
||
lua_pushstring(L, "rgba");
|
||
lua_setfield(L, -2, "type");
|
||
return 1;
|
||
}
|
||
|
||
static int drawRect(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA, LUA_TTABLE });
|
||
Renderer* p = (Renderer*)lua_touserdata(L, 1);
|
||
SDL_Rect r;
|
||
if (LuaRectPointToRect(L, 2, r) < 0)
|
||
{
|
||
luaL_error(L, "Bad argument #%d. Rect expected, got %s", 2, lua_typename(L, lua_type(L, 2)));
|
||
}
|
||
SDL_RenderDrawRect(p->rnd, &r);
|
||
return 0;
|
||
}
|
||
|
||
static int fillRect(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA, LUA_TTABLE });
|
||
Renderer* p = (Renderer*)lua_touserdata(L, 1);
|
||
SDL_Rect r;
|
||
if (LuaRectPointToRect(L, 2, r) < 0)
|
||
{
|
||
luaL_error(L, "Bad argument #%d. Rect expected, got %s", 2, lua_typename(L, lua_type(L, 2)));
|
||
}
|
||
SDL_RenderFillRect(p->rnd, &r);
|
||
return 0;
|
||
}
|
||
|
||
static int loadTexture(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA, LUA_TSTRING });
|
||
Renderer* p = (Renderer*)lua_touserdata(L, 1);
|
||
const char* filepath = lua_tostring(L, 2);
|
||
SDL_Texture* text = IMG_LoadTexture(p->rnd, filepath);
|
||
lua_pushvalue(L, 1);
|
||
lua_pushlightuserdata(L, text);
|
||
Texture::create(L);
|
||
return 1;
|
||
}
|
||
|
||
static int copy(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA, LUA_TUSERDATA, LUA_TTABLE, LUA_TTABLE });
|
||
Renderer* r = (Renderer*)lua_touserdata(L, 1);
|
||
Texture* t = (Texture*)lua_touserdata(L, 2);
|
||
SDL_Rect src;
|
||
int ret = LuaRectPointToRect(L, 3, src);
|
||
if (ret <= 0)
|
||
{
|
||
luaL_error(L, "Bad argument #%d. Rect expected, got %s", 3, lua_typename(L, lua_type(L, 3)));
|
||
}
|
||
SDL_Rect dst;
|
||
ret = LuaRectPointToRect(L, 4, dst);
|
||
if (ret <= 0)
|
||
{
|
||
luaL_error(L, "Bad argument #%d. Rect expected, got %s", 4, lua_typename(L, lua_type(L, 4)));
|
||
}
|
||
SDL_RenderCopy(r->rnd, t->text, &src, &dst);
|
||
return 0;
|
||
}
|
||
|
||
static int copyTo(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA, LUA_TUSERDATA, LUA_TTABLE });
|
||
Renderer* r = (Renderer*)lua_touserdata(L, 1);
|
||
Texture* t = (Texture*)lua_touserdata(L, 2);
|
||
SDL_Rect rect;
|
||
int ret = LuaRectPointToRect(L, 3, rect);
|
||
if (ret < 0)
|
||
{
|
||
luaL_error(L, "Bad argument #%d. Point or Rect expected, got %s", 3, lua_typename(L, lua_type(L, 3)));
|
||
}
|
||
if (ret == 1)
|
||
{
|
||
rect.w = t->w;
|
||
rect.h = t->h;
|
||
}
|
||
SDL_RenderCopy(r->rnd, t->text, NULL, &rect);
|
||
return 0;
|
||
}
|
||
|
||
static int copyFill(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA, LUA_TUSERDATA, LUA_TTABLE });
|
||
Renderer* r = (Renderer*)lua_touserdata(L, 1);
|
||
Texture* t = (Texture*)lua_touserdata(L, 2);
|
||
SDL_Rect rect;
|
||
int ret = LuaRectPointToRect(L, 3, rect);
|
||
if (ret < 0)
|
||
{
|
||
luaL_error(L, "Bad argument #%d. Point or Rect expected, got %s", 3, lua_typename(L, lua_type(L, 3)));
|
||
}
|
||
if (ret == 1)
|
||
{
|
||
rect.w = t->w;
|
||
rect.h = t->h;
|
||
}
|
||
SDL_RenderCopy(r->rnd, t->text, &rect, NULL);
|
||
return 0;
|
||
}
|
||
|
||
static int create(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA });
|
||
cout << "In Renderer::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");
|
||
setfn(L, setColor, "setColor");
|
||
setfn(L, getColor, "getColor");
|
||
setfn(L, drawRect, "drawRect");
|
||
setfn(L, fillRect, "fillRect");
|
||
setfn(L, loadTexture, "loadTexture");
|
||
setfn(L, copy, "copy");
|
||
setfn(L, copyTo, "copyTo");
|
||
|
||
// 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);
|
||
// <20><EFBFBD>ǰRenderer<65>Ķ<EFBFBD><C4B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><>ֹSDL_Window<6F><77><EFBFBD><EFBFBD>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD>.
|
||
lua_pushvalue(L, 1);
|
||
lua_setuservalue(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;
|
||
}
|
||
};
|
||
|
||
struct Font
|
||
{
|
||
TTF_Font* font;
|
||
|
||
static int close(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA });
|
||
cout << "In Font::close" << endl;
|
||
Font* f = (Font*)lua_touserdata(L, 1);
|
||
if (f->font)
|
||
{
|
||
TTF_CloseFont(f->font);
|
||
f->font = nullptr;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static int renderText(lua_State* L)
|
||
{
|
||
Renderer* r = nullptr;
|
||
Font* f = nullptr;
|
||
const char* text = nullptr;
|
||
SDL_Color c;
|
||
if (lua_gettop(L) >= 4)
|
||
{
|
||
check(L, { LUA_TUSERDATA, LUA_TUSERDATA, LUA_TSTRING, LUA_TTABLE });
|
||
f = (Font*)lua_touserdata(L, 1);
|
||
r = (Renderer*)lua_touserdata(L, 2);
|
||
text = lua_tostring(L, 3);
|
||
if (LuaColorToColor(L, 4, c) < 0)
|
||
{
|
||
luaL_error(L, "Bad argument #%d. Color expected, got %s", 4, lua_typename(L, lua_type(L, 4)));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
check(L, { LUA_TUSERDATA, LUA_TSTRING, LUA_TTABLE });
|
||
f = (Font*)lua_touserdata(L, 1);
|
||
text = lua_tostring(L, 3);
|
||
if (LuaColorToColor(L, 4, c) < 0)
|
||
{
|
||
luaL_error(L, "Bad argument #%d. Color expected, got %s", 4, lua_typename(L, lua_type(L, 4)));
|
||
}
|
||
}
|
||
|
||
SDL_Surface* surf = TTF_RenderText_Blended(f->font, text, c);
|
||
|
||
if (r)
|
||
{
|
||
SDL_Texture* text = SDL_CreateTextureFromSurface(r->rnd, surf);
|
||
SDL_FreeSurface(surf);
|
||
lua_pushvalue(L, 2);
|
||
lua_pushlightuserdata(L, text);
|
||
Texture::create(L);
|
||
}
|
||
else
|
||
{
|
||
lua_pushlightuserdata(L, surf);
|
||
Surface::create(L);
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
static int create(lua_State* L)
|
||
{
|
||
check(L, { LUA_TSTRING, LUA_TNUMBER });
|
||
cout << "In Font::create" << endl;
|
||
Font* f = (Font*)lua_newuserdata(L, sizeof(Font));
|
||
lua_getfield(L, LUA_REGISTRYINDEX, "__font_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, "font"); lua_setfield(L, -2, "type");
|
||
setfn(L, close, "close");
|
||
setfn(L, renderText, "renderText");
|
||
|
||
// Set __index of metatable.
|
||
lua_setfield(L, -2, "__index");
|
||
|
||
lua_setfield(L, LUA_REGISTRYINDEX, "__font_mt");
|
||
lua_getfield(L, LUA_REGISTRYINDEX, "__font_mt");
|
||
}
|
||
lua_setmetatable(L, -2);
|
||
const char* filename = lua_tostring(L, 1);
|
||
int fontsz = lua_tointeger(L, 2);
|
||
f->font = TTF_OpenFont(filename, fontsz);
|
||
|
||
return 1;
|
||
}
|
||
};
|
||
|
||
Mix_Chunk* Mix_LoadWAVEx(const char* filepath)
|
||
{
|
||
SDL_RWops* fileData = SDL_RWFromFile(filepath, "rb");
|
||
if (!fileData)
|
||
{
|
||
std::cout << "Mix_LoadWAVEx() failed to call SDL_RWFromFile().\n\t\"" << SDL_GetError() << "\"" << std::endl;
|
||
return NULL;
|
||
}
|
||
|
||
Mix_Chunk* chunk = Mix_LoadWAV_RW(fileData, 1);
|
||
if (!chunk)
|
||
{
|
||
std::cout << "Mix_LoadWAVEx() failed to call Mix_LoadWAV_RW().\n\t\"" << SDL_GetError() << "\"" << std::endl;
|
||
return NULL;
|
||
}
|
||
|
||
return chunk;
|
||
}
|
||
|
||
struct Chunk
|
||
{
|
||
Mix_Chunk* chunk;
|
||
|
||
static int close(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA });
|
||
cout << "In Chunk::close" << endl;
|
||
|
||
Chunk* c = (Chunk*)lua_touserdata(L, 1);
|
||
if (c->chunk)
|
||
{
|
||
Mix_FreeChunk(c->chunk);
|
||
c->chunk = nullptr;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int setVolume(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA, LUA_TNUMBER });
|
||
Chunk* c = (Chunk*)lua_touserdata(L, 1);
|
||
Mix_VolumeChunk(c->chunk, lua_tointeger(L, 2));
|
||
return 0;
|
||
}
|
||
|
||
static int create(lua_State* L)
|
||
{
|
||
check(L, { LUA_TSTRING });
|
||
cout << "In Chunk::create" << endl;
|
||
|
||
Chunk* c = (Chunk*)lua_newuserdata(L, sizeof(Chunk));
|
||
lua_getfield(L, LUA_REGISTRYINDEX, "__chunk_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, "chunk"); lua_setfield(L, -2, "type");
|
||
setfn(L, close, "close");
|
||
setfn(L, setVolume, "setVolumne");
|
||
|
||
// Set __index of metatable.
|
||
lua_setfield(L, -2, "__index");
|
||
|
||
lua_setfield(L, LUA_REGISTRYINDEX, "__chunk_mt");
|
||
lua_getfield(L, LUA_REGISTRYINDEX, "__chunk_mt");
|
||
}
|
||
lua_setmetatable(L, -2);
|
||
|
||
const char* filename = lua_tostring(L, 1);
|
||
cout << "LoadWAV: " << filename << endl;
|
||
c->chunk = Mix_LoadWAVEx(filename);
|
||
cout << "Load chunk error: " << SDL_GetError() << endl;
|
||
|
||
return 1;
|
||
}
|
||
};
|
||
|
||
struct Music
|
||
{
|
||
Mix_Music* music;
|
||
|
||
static int close(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA });
|
||
cout << "In Music::close" << endl;
|
||
|
||
Music* m = (Music*)lua_touserdata(L, 1);
|
||
if (m->music)
|
||
{
|
||
Mix_FreeMusic(m->music);
|
||
m->music = nullptr;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static int create(lua_State* L)
|
||
{
|
||
check(L, { LUA_TSTRING });
|
||
cout << "In Music::create" << endl;
|
||
|
||
Music* m = (Music*)lua_newuserdata(L, sizeof(Music));
|
||
lua_getfield(L, LUA_REGISTRYINDEX, "__music_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, "music"); lua_setfield(L, -2, "type");
|
||
setfn(L, close, "close");
|
||
|
||
// Set __index of metatable.
|
||
lua_setfield(L, -2, "__index");
|
||
|
||
lua_setfield(L, LUA_REGISTRYINDEX, "__music_mt");
|
||
lua_getfield(L, LUA_REGISTRYINDEX, "__music_mt");
|
||
}
|
||
lua_setmetatable(L, -2);
|
||
|
||
const char* filename = lua_tostring(L, 1);
|
||
m->music = Mix_LoadMUS(filename);
|
||
cout << "Load music error: " << SDL_GetError() << endl;
|
||
|
||
return 1;
|
||
}
|
||
};
|
||
|
||
struct MusicPlayer
|
||
{
|
||
static int close(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA });
|
||
cout << "In MusicPlayer::close" << endl;
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int playMusic(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA, LUA_TUSERDATA });
|
||
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
|
||
Music* m = (Music*)lua_touserdata(L, 2);
|
||
cout << "Play music ret: " << Mix_PlayMusic(m->music, 1) << " " << SDL_GetError() << endl;
|
||
return 0;
|
||
}
|
||
|
||
static int resumeMusic(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA });
|
||
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
|
||
Mix_ResumeMusic();
|
||
return 0;
|
||
}
|
||
|
||
static int stopMusic(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA });
|
||
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
|
||
Mix_HaltMusic();
|
||
return 0;
|
||
}
|
||
|
||
static int pauseMusic(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA });
|
||
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
|
||
Mix_PauseMusic();
|
||
return 0;
|
||
}
|
||
|
||
static int rewindMusic(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA });
|
||
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
|
||
Mix_RewindMusic();
|
||
return 0;
|
||
}
|
||
|
||
static int fadeInMusic(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA, LUA_TUSERDATA , LUA_TNUMBER });
|
||
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
|
||
Music* m = (Music*)lua_touserdata(L, 2);
|
||
cout << "Fade in music ret: " << Mix_FadeInMusic(m->music, 1, lua_tointeger(L, 3)) << " " << SDL_GetError() << endl;
|
||
return 0;
|
||
}
|
||
|
||
static int fadeInMusicPos(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA, LUA_TUSERDATA , LUA_TNUMBER, LUA_TNUMBER });
|
||
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
|
||
Music* m = (Music*)lua_touserdata(L, 2);
|
||
cout << "Fade in music ret: " << Mix_FadeInMusicPos(m->music, 1, lua_tointeger(L, 3), lua_tonumber(L, 4)) << " " << SDL_GetError() << endl;
|
||
return 0;
|
||
}
|
||
|
||
static int fadeOutMusic(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA, LUA_TNUMBER });
|
||
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
|
||
cout << "Fade out music ret: " << Mix_FadeOutMusic(lua_tointeger(L, 2)) << " " << SDL_GetError() << endl;
|
||
return 0;
|
||
}
|
||
|
||
static int setMusicVolume(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA, LUA_TNUMBER });
|
||
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
|
||
Mix_VolumeMusic(lua_tointeger(L, 2));
|
||
return 0;
|
||
}
|
||
|
||
static int setMusicPos(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA, LUA_TNUMBER });
|
||
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
|
||
Mix_SetMusicPosition(lua_tonumber(L, 2));
|
||
return 0;
|
||
}
|
||
|
||
static int isPlayingMusic(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA });
|
||
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
|
||
lua_pushboolean(L, Mix_PlayingMusic());
|
||
return 1;
|
||
}
|
||
|
||
static int isPausedMusic(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA });
|
||
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
|
||
lua_pushboolean(L, Mix_PausedMusic());
|
||
return 1;
|
||
}
|
||
|
||
static int isFadingMusic(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA });
|
||
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
|
||
int ret = 0;
|
||
switch (Mix_FadingMusic())
|
||
{
|
||
case MIX_NO_FADING:
|
||
ret = 0;
|
||
break;
|
||
case MIX_FADING_OUT:
|
||
ret = -1;
|
||
break;
|
||
case MIX_FADING_IN:
|
||
return 1;
|
||
}
|
||
lua_pushinteger(L, ret);
|
||
return 1;
|
||
}
|
||
|
||
static int playChannel(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA, LUA_TUSERDATA, LUA_TNUMBER, LUA_TNUMBER});
|
||
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
|
||
Chunk* c = (Chunk*)lua_touserdata(L, 2);
|
||
cout << "Play channel ret: " << Mix_PlayChannel(lua_tointeger(L, 3), c->chunk, lua_tointeger(L, 4)) << " " << SDL_GetError() << endl;
|
||
return 0;
|
||
}
|
||
|
||
static int fadeInChannel(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA, LUA_TUSERDATA, LUA_TNUMBER, LUA_TNUMBER, LUA_TNUMBER });
|
||
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
|
||
Chunk* c = (Chunk*)lua_touserdata(L, 2);
|
||
Mix_FadeInChannel(lua_tointeger(L, 3), c->chunk, lua_tointeger(L, 4), lua_tointeger(L, 5));
|
||
return 0;
|
||
}
|
||
|
||
static int fadeOutChannel(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA, LUA_TNUMBER, LUA_TNUMBER });
|
||
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
|
||
Mix_FadeOutChannel(lua_tointeger(L, 2), lua_tointeger(L, 3));
|
||
return 0;
|
||
}
|
||
|
||
static int stopChannel(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA, LUA_TNUMBER });
|
||
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
|
||
Mix_HaltChannel(lua_tointeger(L, 2));
|
||
return 0;
|
||
}
|
||
|
||
static int resumeChannel(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA, LUA_TNUMBER });
|
||
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
|
||
Mix_Resume(lua_tointeger(L, 2));
|
||
return 0;
|
||
}
|
||
|
||
static int pauseChannel(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA, LUA_TNUMBER });
|
||
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
|
||
Mix_Pause(lua_tointeger(L, 2));
|
||
return 0;
|
||
}
|
||
|
||
static int isPlayingChannel(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA, LUA_TNUMBER });
|
||
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
|
||
lua_pushinteger(L, Mix_Playing(lua_tointeger(L, 2)));
|
||
return 1;
|
||
}
|
||
|
||
static int isPausedChannel(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA, LUA_TNUMBER });
|
||
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
|
||
lua_pushinteger(L, Mix_Paused(lua_tointeger(L, 2)));
|
||
return 1;
|
||
}
|
||
|
||
static int isFadingChannel(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA, LUA_TNUMBER });
|
||
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
|
||
int ret = 0;
|
||
switch (Mix_FadingChannel(lua_tointeger(L, 2)))
|
||
{
|
||
case MIX_NO_FADING:
|
||
ret = 0;
|
||
break;
|
||
case MIX_FADING_OUT:
|
||
ret = -1;
|
||
break;
|
||
case MIX_FADING_IN:
|
||
return 1;
|
||
}
|
||
lua_pushinteger(L, ret);
|
||
return 1;
|
||
}
|
||
|
||
static int setChannelVolume(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA, LUA_TNUMBER, LUA_TNUMBER });
|
||
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
|
||
Mix_Volume(lua_tointeger(L, 2), lua_tointeger(L, 3));
|
||
return 0;
|
||
}
|
||
|
||
static int setChannelDistance(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA, LUA_TNUMBER, LUA_TNUMBER });
|
||
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
|
||
Mix_SetDistance(lua_tointeger(L, 2), lua_tointeger(L, 3));
|
||
return 0;
|
||
}
|
||
|
||
static int setChannelPosition(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA, LUA_TNUMBER, LUA_TNUMBER, LUA_TNUMBER });
|
||
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
|
||
Sint16 angle = lua_tonumber(L, 3);
|
||
unsigned int distance = lua_tonumber(L, 4);
|
||
cout << "Mix_SetPosition " << lua_tointeger(L, 2) << " " << angle << " " << distance << endl;
|
||
if (0 == Mix_SetPosition(lua_tointeger(L, 2), angle, distance))
|
||
{
|
||
LuaSDLError(L);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static int setChannelPanning(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA, LUA_TNUMBER, LUA_TNUMBER, LUA_TNUMBER });
|
||
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
|
||
Mix_SetPanning(lua_tointeger(L, 2), lua_tointeger(L, 3), lua_tointeger(L, 4));
|
||
return 0;
|
||
}
|
||
|
||
static int setTotalChannel(lua_State* L)
|
||
{
|
||
check(L, { LUA_TUSERDATA, LUA_TNUMBER });
|
||
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
|
||
Mix_AllocateChannels(lua_tointeger(L, 2));
|
||
return 0;
|
||
}
|
||
|
||
static int create(lua_State* L)
|
||
{
|
||
cout << "In MusicPlayer::create" << endl;
|
||
|
||
MusicPlayer* m = (MusicPlayer*)lua_newuserdata(L, sizeof(MusicPlayer));
|
||
lua_getfield(L, LUA_REGISTRYINDEX, "__musicplayer_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, "musicplayer"); lua_setfield(L, -2, "type");
|
||
setfn(L, close, "close");
|
||
setfn(L, playMusic, "playMusic");
|
||
setfn(L, resumeMusic, "resumeMusic");
|
||
setfn(L, pauseMusic, "pauseMusic");
|
||
setfn(L, stopMusic, "stopMusic");
|
||
setfn(L, rewindMusic, "rewindMusic");
|
||
setfn(L, fadeInMusic, "fadeInMusic");
|
||
setfn(L, fadeOutMusic, "fadeOutMusic");
|
||
setfn(L, fadeInMusicPos, "fadeInMusicPos");
|
||
|
||
setfn(L, setMusicVolume, "setMusicVolume");
|
||
setfn(L, setMusicPos, "setMusicPos");
|
||
|
||
setfn(L, isPlayingMusic, "isPlayingMusic");
|
||
setfn(L, isPausedMusic, "isPausedMusic");
|
||
setfn(L, isFadingMusic, "isFadingMusic");
|
||
|
||
setfn(L, playChannel, "playChannel");
|
||
setfn(L, resumeChannel, "resumeChannel");
|
||
setfn(L, pauseChannel, "pauseChannel");
|
||
setfn(L, stopChannel, "stopChannel");
|
||
setfn(L, fadeInChannel, "fadeInChannel");
|
||
setfn(L, fadeOutChannel, "fadeOutChannel");
|
||
|
||
setfn(L, setChannelVolume, "setChannelVolume");
|
||
setfn(L, setChannelDistance, "setChannelDistance");
|
||
setfn(L, setChannelPosition, "setChannelPosition");
|
||
setfn(L, setChannelPanning, "setChannelPanning");
|
||
setfn(L, setTotalChannel, "setTotalChannel");
|
||
|
||
setfn(L, isPlayingChannel, "isPlayingChannel");
|
||
setfn(L, isPausedChannel, "isPausedChannel");
|
||
setfn(L, isFadingChannel, "isFadingChannel");
|
||
|
||
// Set __index of metatable.
|
||
lua_setfield(L, -2, "__index");
|
||
|
||
lua_setfield(L, LUA_REGISTRYINDEX, "__musicplayer_mt");
|
||
lua_getfield(L, LUA_REGISTRYINDEX, "__musicplayer_mt");
|
||
}
|
||
lua_setmetatable(L, -2);
|
||
|
||
return 1;
|
||
}
|
||
};
|
||
|
||
struct LibFS
|
||
{
|
||
static int listdir(lua_State* L)
|
||
{
|
||
check(L, { LUA_TSTRING });
|
||
vector<FileInfo> vec = ListDir(lua_tostring(L, 1));
|
||
int sz = vec.size();
|
||
lua_newtable(L);
|
||
for (lua_Integer i = 0; i < sz; i++)
|
||
{
|
||
const FileInfo& info = vec[i];
|
||
lua_newtable(L);
|
||
lua_pushstring(L, info.name.c_str());
|
||
lua_setfield(L, -2, "name");
|
||
lua_pushstring(L, info.type ? "dir" : "file");
|
||
lua_setfield(L, -2, "type");
|
||
lua_pushinteger(L, info.size);
|
||
lua_setfield(L, -2, "size");
|
||
lua_seti(L, -2, i + 1);
|
||
}
|
||
return 1;
|
||
}
|
||
static int create(lua_State* L)
|
||
{
|
||
lua_newtable(L);
|
||
setfn(L, listdir, "listdir");
|
||
|
||
return 1;
|
||
}
|
||
};
|
||
|
||
|
||
void check_init(lua_State* L, const char* name, int ret, int expected = 0)
|
||
{
|
||
if (ret != expected)
|
||
{
|
||
luaL_error(L, "Failed to initialize %s. ret: %d. expected: %d. SDLError: %s", name, ret, expected, SDL_GetError());
|
||
}
|
||
}
|
||
|
||
void _Global_On_Music_Finished()
|
||
{
|
||
SDL_Event e;
|
||
e.type = SDL_USEREVENT;
|
||
e.user.code = 1003;
|
||
SDL_PushEvent(&e);
|
||
}
|
||
|
||
void _Global_On_Channel_Finished(int channel)
|
||
{
|
||
SDL_Event e;
|
||
e.type = SDL_USEREVENT;
|
||
e.user.code = 1004;
|
||
e.user.data1 = new int(channel);
|
||
SDL_PushEvent(&e);
|
||
}
|
||
|
||
static int setTimeout(lua_State* L)
|
||
{
|
||
check(L, { LUA_TFUNCTION, LUA_TNUMBER }); // setTimeout(cb: function, ms: number)
|
||
|
||
LuaTimerCallbackData* pd = new LuaTimerCallbackData();
|
||
pd->p = nullptr;
|
||
pd->type = 0;
|
||
pd->id = SDL_AddTimer(lua_tointeger(L, 2), LuaTimerCallbackGate, pd);
|
||
|
||
lua_pushlightuserdata(L, &setTimeout);
|
||
if (lua_gettable(L, LUA_REGISTRYINDEX) != LUA_TTABLE)
|
||
{
|
||
lua_pop(L, 1);
|
||
|
||
lua_pushlightuserdata(L, &setTimeout);
|
||
lua_newtable(L);
|
||
lua_pushvalue(L, 1);
|
||
lua_seti(L, -2, pd->id); // { [timerID] = callback }
|
||
|
||
lua_settable(L, LUA_REGISTRYINDEX); // registry[&setTimeout] = timerCallbackTable
|
||
}
|
||
else
|
||
{
|
||
lua_pushvalue(L, 1);
|
||
lua_seti(L, -2, pd->id); // timerCallbackTable[timerID] = callback
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static int setInterval(lua_State* L)
|
||
{
|
||
check(L, { LUA_TFUNCTION, LUA_TNUMBER }); // setTimeout(cb: function, ms: number)
|
||
|
||
LuaTimerCallbackData* pd = new LuaTimerCallbackData();
|
||
pd->p = nullptr;
|
||
pd->type = 1;
|
||
pd->id = SDL_AddTimer(lua_tointeger(L, 2), LuaTimerCallbackGate, pd);
|
||
|
||
lua_pushlightuserdata(L, &setTimeout);
|
||
if (lua_gettable(L, LUA_REGISTRYINDEX) != LUA_TTABLE)
|
||
{
|
||
lua_pop(L, 1);
|
||
|
||
lua_pushlightuserdata(L, &setTimeout);
|
||
lua_newtable(L);
|
||
lua_pushvalue(L, 1);
|
||
lua_seti(L, -2, pd->id); // { [timerID] = callback }
|
||
|
||
lua_settable(L, LUA_REGISTRYINDEX); // registry[&setTimeout] = timerCallbackTable
|
||
}
|
||
else
|
||
{
|
||
lua_pushvalue(L, 1);
|
||
lua_seti(L, -2, pd->id); // timerCallbackTable[timerID] = callback
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static int evFireExit(lua_State* L)
|
||
{
|
||
SDL_Event e;
|
||
e.type = SDL_QUIT;
|
||
e.quit.timestamp = SDL_GetTicks();
|
||
SDL_PushEvent(&e);
|
||
return 0;
|
||
}
|
||
|
||
#define reg_in_lua(L, cfunc, name) lua_pushcfunction(L, cfunc); lua_setglobal(L, name)
|
||
|
||
int InitEngine(lua_State* L)
|
||
{
|
||
int ret;
|
||
ret = SDL_Init(SDL_INIT_EVERYTHING);
|
||
check_init(L, "SDL", ret);
|
||
ret = IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG);
|
||
check_init(L, "IMG", ret, IMG_INIT_JPG | IMG_INIT_PNG);
|
||
ret = TTF_Init();
|
||
check_init(L, "TTF", ret);
|
||
ret = Mix_Init(MIX_INIT_MP3 | MIX_INIT_MOD | MIX_INIT_OGG );
|
||
check_init(L, "MIX", ret, MIX_INIT_MP3 | MIX_INIT_MOD | MIX_INIT_OGG);
|
||
|
||
ret = Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 512);
|
||
check_init(L, "AUDIO", ret);
|
||
Mix_AllocateChannels(16);
|
||
Mix_HookMusicFinished(_Global_On_Music_Finished);
|
||
Mix_ChannelFinished(_Global_On_Channel_Finished);
|
||
|
||
reg_in_lua(L, Window::create, "Window");
|
||
reg_in_lua(L, Renderer::create, "Renderer");
|
||
reg_in_lua(L, Font::create, "Font");
|
||
reg_in_lua(L, Chunk::create, "Chunk");
|
||
reg_in_lua(L, Music::create, "Music");
|
||
reg_in_lua(L, MusicPlayer::create, "MusicPlayer");
|
||
|
||
// Global functions
|
||
reg_in_lua(L, ConvertMusicAsync, "ConvertMusic");
|
||
reg_in_lua(L, setTimeout, "SetTimeout");
|
||
reg_in_lua(L, setInterval, "SetInterval");
|
||
reg_in_lua(L, evFireExit, "FireExit");
|
||
|
||
// Global Library
|
||
LibFS::create(L); lua_setglobal(L, "fs");
|
||
InitEnum(L);
|
||
InitNetwork(L);
|
||
|
||
|
||
// Registry table
|
||
lua_pushlightuserdata(L, &StartEngine);
|
||
lua_newtable(L);
|
||
lua_settable(L, LUA_REGISTRYINDEX);
|
||
|
||
return 0;
|
||
}
|
||
|
||
int LuaRunCallback(lua_State* L, int nargs, int nresults = LUA_MULTRET)
|
||
{
|
||
int ret = lua_pcall(L, nargs, nresults, 0);
|
||
if (ret)
|
||
{
|
||
switch (ret)
|
||
{
|
||
case LUA_ERRRUN:
|
||
cout << "LuaRunError: " << lua_tostring(L, -1) << endl;
|
||
break;
|
||
case LUA_ERRMEM:
|
||
cout << "LuaMemError: " << lua_tostring(L, -1) << endl;
|
||
break;
|
||
case LUA_ERRERR:
|
||
cout << "LuaRecursiveError: " << lua_tostring(L, -1) << endl;
|
||
break;
|
||
case LUA_ERRGCMM:
|
||
cout << "LuaGCError: " << lua_tostring(L, -1) << endl;
|
||
break;
|
||
default:
|
||
cout << "LuaError: " << lua_tostring(L, -1) << endl;
|
||
break;
|
||
}
|
||
lua_pop(L, 1);
|
||
return -1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
int StartEngine(lua_State* L)
|
||
{
|
||
// The Event Loop
|
||
bool running = true;
|
||
|
||
SDL_Event e;
|
||
while (running && SDL_WaitEvent(&e))
|
||
{
|
||
switch (e.type)
|
||
{
|
||
case SDL_MOUSEBUTTONDOWN:
|
||
{
|
||
SDL_Window* wnd = SDL_GetWindowFromID(e.button.windowID);
|
||
if (wnd)
|
||
{
|
||
lua_pushlightuserdata(L, wnd);
|
||
lua_gettable(L, LUA_REGISTRYINDEX);
|
||
if (lua_getfield(L, -1, "onmousedown") != LUA_TNIL)
|
||
{
|
||
lua_pushnumber(L, e.button.x);
|
||
lua_pushnumber(L, e.button.y);
|
||
lua_pushnumber(L, e.button.button);
|
||
const char* arr[8];
|
||
int ret = GetMouseState(arr, e.button.state);
|
||
lua_newtable(L);
|
||
for (int i = 0; i < ret; i++)
|
||
{
|
||
lua_pushboolean(L, 1);
|
||
lua_setfield(L, -2, arr[i]);
|
||
}
|
||
LuaRunCallback(L, 4);
|
||
}
|
||
else lua_pop(L, 1);
|
||
lua_pop(L, 1);
|
||
}
|
||
break;
|
||
}
|
||
case SDL_MOUSEBUTTONUP:
|
||
{
|
||
SDL_Window* wnd = SDL_GetWindowFromID(e.button.windowID);
|
||
if (wnd)
|
||
{
|
||
lua_pushlightuserdata(L, wnd);
|
||
lua_gettable(L, LUA_REGISTRYINDEX);
|
||
if (lua_getfield(L, -1, "onmouseup") != LUA_TNIL)
|
||
{
|
||
lua_pushnumber(L, e.button.x);
|
||
lua_pushnumber(L, e.button.y);
|
||
lua_pushnumber(L, e.button.button);
|
||
const char* arr[8];
|
||
int ret = GetMouseState(arr, e.button.state);
|
||
lua_newtable(L);
|
||
for (int i = 0; i < ret; i++)
|
||
{
|
||
lua_pushboolean(L, 1);
|
||
lua_setfield(L, -2, arr[i]);
|
||
}
|
||
LuaRunCallback(L, 4);
|
||
}
|
||
else lua_pop(L, 1);
|
||
lua_pop(L, 1);
|
||
}
|
||
break;
|
||
}
|
||
case SDL_MOUSEMOTION:
|
||
{
|
||
SDL_Window* wnd = SDL_GetWindowFromID(e.motion.windowID);
|
||
if (wnd)
|
||
{
|
||
lua_pushlightuserdata(L, wnd);
|
||
lua_gettable(L, LUA_REGISTRYINDEX);
|
||
if (lua_getfield(L, -1, "onmousemove") != LUA_TNIL)
|
||
{
|
||
lua_pushnumber(L, e.motion.x);
|
||
lua_pushnumber(L, e.motion.y);
|
||
lua_pushnumber(L, e.motion.xrel);
|
||
lua_pushnumber(L, e.motion.yrel);
|
||
const char* arr[8];
|
||
int ret = GetMouseState(arr, e.button.state);
|
||
lua_newtable(L);
|
||
for (int i = 0; i < ret; i++)
|
||
{
|
||
lua_pushboolean(L, 1);
|
||
lua_setfield(L, -2, arr[i]);
|
||
}
|
||
LuaRunCallback(L, 5);
|
||
}
|
||
else lua_pop(L, 1);
|
||
lua_pop(L, 1);
|
||
}
|
||
break;
|
||
}
|
||
case SDL_QUIT:
|
||
{
|
||
running = 0;
|
||
|
||
lua_pushlightuserdata(L, &StartEngine);
|
||
lua_gettable(L, LUA_REGISTRYINDEX);
|
||
|
||
if (lua_getfield(L, -1, "onquit") != LUA_TNIL)
|
||
{
|
||
if (!LuaRunCallback(L, 0, 1))
|
||
{
|
||
if (lua_type(L, -1) == LUA_TBOOLEAN && !lua_toboolean(L, -1))
|
||
{
|
||
running = 1;
|
||
}
|
||
lua_pop(L, 1);
|
||
}
|
||
}
|
||
else lua_pop(L, 1);
|
||
|
||
lua_pop(L, 1);
|
||
break;
|
||
}
|
||
case SDL_KEYDOWN:
|
||
{
|
||
SDL_Window* wnd = SDL_GetWindowFromID(e.key.windowID);
|
||
if (wnd)
|
||
{
|
||
lua_pushlightuserdata(L, wnd);
|
||
lua_gettable(L, LUA_REGISTRYINDEX);
|
||
if (lua_getfield(L, -1, "onkeydown") != LUA_TNIL)
|
||
{
|
||
lua_pushnumber(L, e.key.keysym.sym);
|
||
LuaRunCallback(L, 1);
|
||
}
|
||
else lua_pop(L, 1);
|
||
lua_pop(L, 1);
|
||
}
|
||
break;
|
||
}
|
||
case SDL_KEYUP:
|
||
{
|
||
SDL_Window* wnd = SDL_GetWindowFromID(e.key.windowID);
|
||
if (wnd)
|
||
{
|
||
lua_pushlightuserdata(L, wnd);
|
||
lua_gettable(L, LUA_REGISTRYINDEX);
|
||
if (lua_getfield(L, -1, "onkeyup") != LUA_TNIL)
|
||
{
|
||
lua_pushnumber(L, e.key.keysym.sym);
|
||
LuaRunCallback(L, 1);
|
||
}
|
||
else lua_pop(L, 1);
|
||
lua_pop(L, 1);
|
||
}
|
||
break;
|
||
}
|
||
case SDL_USEREVENT:
|
||
{
|
||
switch (e.user.code)
|
||
{
|
||
case 1001:
|
||
{
|
||
// <20><>ʱ<EFBFBD><CAB1><EFBFBD>ص<EFBFBD>
|
||
LuaTimerCallbackData* pd = (LuaTimerCallbackData*)e.user.data1;
|
||
lua_pushlightuserdata(L, &setTimeout);
|
||
lua_gettable(L, LUA_REGISTRYINDEX); // registry[&setTimeout]
|
||
lua_geti(L, -1, pd->id); // registry[&setTimeout][timerID]
|
||
if (!pd->type)
|
||
{
|
||
delete pd;
|
||
LuaRunCallback(L, 0);
|
||
}
|
||
else
|
||
{
|
||
if (LuaRunCallback(L, 0, 1) == 0)
|
||
{
|
||
if (lua_type(L, -1) == LUA_TBOOLEAN && !lua_toboolean(L, -1))
|
||
{
|
||
SDL_RemoveTimer(pd->id);
|
||
delete pd;
|
||
}
|
||
lua_pop(L, 1);
|
||
}
|
||
}
|
||
lua_pop(L, 1);
|
||
break;
|
||
}
|
||
case 1002:
|
||
{
|
||
// ȫ<>ֻص<D6BB>, <20>첽<EFBFBD><ECB2BD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><DEB7><EFBFBD>ֵ
|
||
lua_pushfstring(L, "cb%d", *(int*)e.user.data1);
|
||
lua_gettable(L, LUA_REGISTRYINDEX);
|
||
delete e.user.data1;
|
||
LuaRunCallback(L, 0);
|
||
break;
|
||
}
|
||
case 1003:
|
||
|
||
// <20><><EFBFBD>ֲ<EFBFBD><D6B2><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
lua_pushlightuserdata(L, &StartEngine);
|
||
lua_gettable(L, LUA_REGISTRYINDEX);
|
||
if (lua_getfield(L, -1, "onmusicover") != LUA_TNIL)
|
||
{
|
||
LuaRunCallback(L, 0);
|
||
}
|
||
else lua_pop(L, 1);
|
||
lua_pop(L, 1);
|
||
break;
|
||
}
|
||
case 1004:
|
||
{
|
||
// Ƶ<><C6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
lua_pushlightuserdata(L, &StartEngine);
|
||
lua_gettable(L, LUA_REGISTRYINDEX);
|
||
if (lua_getfield(L, -1, "onchannelover") != LUA_TNIL)
|
||
{
|
||
lua_pushinteger(L, *(int*)e.user.data1);
|
||
delete e.user.data1;
|
||
LuaRunCallback(L, 1);
|
||
}
|
||
else lua_pop(L, 1);
|
||
lua_pop(L, 1);
|
||
break;
|
||
}
|
||
case 1005:
|
||
case 1006:
|
||
{
|
||
HandleNetworkEvent(L, e);
|
||
break;
|
||
}
|
||
break;
|
||
}
|
||
default:
|
||
{
|
||
cout << "Unhandled Event: 0x" << hex << e.type << endl;
|
||
}
|
||
}
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
int StopEngine()
|
||
{
|
||
Mix_CloseAudio();
|
||
Mix_Quit();
|
||
TTF_Quit();
|
||
IMG_Quit();
|
||
SDL_Quit();
|
||
|
||
return 0;
|
||
}
|