LuaEngine v0.5.1

Add api docs.
More support of loading resources from memory.
Minor fix.
This commit is contained in:
Kirigaya Kazuto 2020-06-29 20:38:14 +08:00
parent 8c805bd037
commit 7b098c50ed
4 changed files with 359 additions and 69 deletions

View File

@ -7,16 +7,33 @@
#include <map> #include <map>
using namespace std; using namespace std;
/**
module Channel
create(name: string): Channel, isCreated: boolean
open(name: string): Channel or nil
collect()
class Channel
put(value: nil, number, boolean, string)
get(): nil, number, boolean, string
try_put(value: nil, number, boolean, string, timeout_ms: int): isSuccess: boolean
try_get(timeout_ms: int): isSuccess: boolean, value: nil, number, boolean, string
wait([timeout_ms: int]): hasValue: boolean
Block until has value if no timeout specified. Return false on reaching timeout.
Negative timeout is regarded as 0.
Zero timeout means instant check-and-return.
*/
class Channel class Channel
{ {
public: public:
int capacity; int capacity;
bool closed;
queue<tuple<int, string>> bus; queue<tuple<int, string>> bus;
mutex m; mutex m;
condition_variable cond; condition_variable cond;
Channel(int cap) : capacity(cap), closed(false) Channel(int cap) : capacity(cap)
{ {
} }
@ -35,23 +52,72 @@ int channel_dtor(lua_State* L)
return 0; return 0;
} }
static bool push_to_lua(lua_State* L, int type, const string& value)
{
switch (type)
{
case LUA_TNIL:
lua_pushnil(L);
return true;
case LUA_TNUMBER:
lua_stringtonumber(L, value.c_str());
return true;
case LUA_TBOOLEAN:
lua_pushboolean(L, value.empty());
return true;
case LUA_TSTRING:
lua_pushlstring(L, value.data(), value.size());
return true;
default:
return false;
}
}
static bool get_from_lua(lua_State* L, int index, int& type, string& value)
{
type = lua_type(L, index);
switch (type)
{
case LUA_TNIL:
value = string();
return true;
case LUA_TNUMBER:
value = lua_tostring(L, index);
return true;
case LUA_TBOOLEAN:
{
if (lua_toboolean(L, index))
{
value = "true";
}
else
{
value = string();
}
return true;
}
case LUA_TSTRING:
{
size_t sz;
const char* p = lua_tolstring(L, index, &sz);
value = string(p, sz);
return true;
}
default:
return false;
}
}
// Get a value from channel // Get a value from channel
int channel_get(lua_State* L) int channel_get(lua_State* L)
{ {
auto c = lua_checkblock<LuaChannel>(L, 1, "LuaChannel"); auto c = lua_checkblock<LuaChannel>(L, 1, "LuaChannel");
int type; int type;
string value; string value;
printf("Call get on LuaChannel %p\n", c);
{ {
unique_lock<mutex> ulk(c->sp->m); unique_lock<mutex> ulk(c->sp->m);
if (c->sp->closed)
{
lua_pushnil(L);
lua_pushboolean(L, 1);
return 2;
}
c->sp->cond.wait(ulk, [&]() { return !c->sp->bus.empty(); }); c->sp->cond.wait(ulk, [&]() { return !c->sp->bus.empty(); });
// Un-serialize from string to Lua object // Un-serialize from string to Lua object
@ -60,61 +126,71 @@ int channel_get(lua_State* L)
c->sp->cond.notify_all(); c->sp->cond.notify_all();
} }
switch (type) if (!push_to_lua(L, type, value))
{ {
case LUA_TNIL:
lua_pushnil(L);
break;
case LUA_TNUMBER:
lua_stringtonumber(L, value.c_str());
break;
case LUA_TBOOLEAN:
lua_pushboolean(L, value.empty());
break;
case LUA_TSTRING:
lua_pushlstring(L, value.c_str(), value.size());
break;
default:
return luaL_error(L, "channel_get: unsupported type %s", lua_typename(L, type)); return luaL_error(L, "channel_get: unsupported type %s", lua_typename(L, type));
} }
return 1; return 1;
} }
int channel_try_get(lua_State* L)
{
auto c = lua_checkblock<LuaChannel>(L, 1, "LuaChannel");
int ms = luaL_checkinteger(L, 2);
bool success;
int type;
string value;
{
unique_lock<mutex> ulk(c->sp->m);
if (ms > 0)
{
success = c->sp->cond.wait_for(ulk, chrono::milliseconds(ms), [&]() { return !c->sp->bus.empty(); });
}
else
{
success = !c->sp->bus.empty();
}
if (success)
{
tie(type, value) = c->sp->bus.front();
c->sp->bus.pop();
c->sp->cond.notify_all();
}
}
if (success)
{
lua_pushboolean(L, true);
if (!push_to_lua(L, type, value))
{
return luaL_error(L, "channel_try_get: unsupported type %s", lua_typename(L, type));
}
return 2;
}
else
{
lua_pushboolean(L, false);
return 1;
}
}
// Push a value to channel. Types are limited to nil, boolean, number, string. // Push a value to channel. Types are limited to nil, boolean, number, string.
int channel_put(lua_State* L) int channel_put(lua_State* L)
{ {
auto c = lua_checkblock<LuaChannel>(L, 1, "LuaChannel"); auto c = lua_checkblock<LuaChannel>(L, 1, "LuaChannel");
int type = lua_type(L, 2);
int type;
string value; string value;
switch (type) if (!get_from_lua(L, 2, type, value))
{ {
case LUA_TNIL:
break;
case LUA_TNUMBER:
value = lua_tostring(L, 2);
break;
case LUA_TBOOLEAN:
if (lua_toboolean(L, 2))
{
value = "true";
}
break;
case LUA_TSTRING:
value = lua_tostring(L, 2);
break;
default:
return luaL_error(L, "channel_put: unsupported type %s", lua_typename(L, type)); return luaL_error(L, "channel_put: unsupported type %s", lua_typename(L, type));
} }
printf("Call put on LuaChannel %p\n", c);
{ {
unique_lock<mutex> ulk(c->sp->m); unique_lock<mutex> ulk(c->sp->m);
if (c->sp->closed)
{
return luaL_error(L, "channel_put: cannot put to closed channel.");
}
c->sp->cond.wait(ulk, [&]() { return (c->sp->capacity > 0 && c->sp->bus.size() < c->sp->capacity) || (c->sp->capacity == 0 && c->sp->bus.empty()); }); c->sp->cond.wait(ulk, [&]() { return (c->sp->capacity > 0 && c->sp->bus.size() < c->sp->capacity) || (c->sp->capacity == 0 && c->sp->bus.empty()); });
c->sp->bus.emplace(type, value); c->sp->bus.emplace(type, value);
c->sp->cond.notify_all(); c->sp->cond.notify_all();
@ -123,6 +199,77 @@ int channel_put(lua_State* L)
return 0; return 0;
} }
int channel_try_put(lua_State* L)
{
auto c = lua_checkblock<LuaChannel>(L, 1, "LuaChannel");
int ms = luaL_checkinteger(L, 2);
bool success;
int type;
string value;
if (!get_from_lua(L, 3, type, value))
{
return luaL_error(L, "channel_try_put: unsupported type %s", lua_typename(L, type));
}
{
unique_lock<mutex> ulk(c->sp->m);
if (ms > 0)
{
success = c->sp->cond.wait_for(ulk, chrono::milliseconds(ms), [&]() { return (c->sp->capacity > 0 && c->sp->bus.size() < c->sp->capacity) || (c->sp->capacity == 0 && c->sp->bus.empty()); });
}
else
{
success = (c->sp->capacity > 0 && c->sp->bus.size() < c->sp->capacity) || (c->sp->capacity == 0 && c->sp->bus.empty());
}
if (success)
{
c->sp->bus.emplace(type, value);
c->sp->cond.notify_all();
}
}
lua_pushboolean(L, success);
return 1;
}
int channel_wait(lua_State* L)
{
auto c = lua_checkblock<LuaChannel>(L, 1, "LuaChannel");
int ms = -1;
if (!lua_isnone(L, 2))
{
ms = luaL_checkinteger(L, 2);
if (ms < 0)
{
ms = 0;
}
}
bool hasValue;
{
unique_lock<mutex> ulk(c->sp->m);
if (ms > 0)
{
hasValue = c->sp->cond.wait_for(ulk, chrono::milliseconds(ms), [&]() { return !c->sp->bus.empty(); });
}
else if (ms == 0)
{
hasValue = !c->sp->bus.empty();
}
else
{
c->sp->cond.wait(ulk, [&]() { return !c->sp->bus.empty(); });
hasValue = true;
}
}
lua_pushboolean(L, hasValue);
return 1;
}
// This will push a LuaChannel userdata onto the lvm stack. // This will push a LuaChannel userdata onto the lvm stack.
void put_channel(lua_State* L, const shared_ptr<Channel>& spChan) void put_channel(lua_State* L, const shared_ptr<Channel>& spChan)
{ {
@ -136,14 +283,17 @@ void put_channel(lua_State* L, const shared_ptr<Channel>& spChan)
lua_newtable(L); lua_newtable(L);
lua_setfield_function(L, "get", channel_get); lua_setfield_function(L, "get", channel_get);
lua_setfield_function(L, "put", channel_put); lua_setfield_function(L, "put", channel_put);
lua_setfield_function(L, "try_get", channel_try_get);
lua_setfield_function(L, "try_put", channel_try_put);
lua_setfield_function(L, "wait", channel_wait);
lua_setfield(L, -2, "__index"); lua_setfield(L, -2, "__index");
} }
lua_setmetatable(L, -2); lua_setmetatable(L, -2);
} }
// Global Variables for managing LuaChannel openning and creating // Global Variables for managing LuaChannel openning and creating
mutex gChannelNSLock; mutex globalChannelNSLock;
map<string, weak_ptr<Channel>> gChannelNS; map<string, weak_ptr<Channel>> globalChannelNS;
int channel_open(lua_State* L) int channel_open(lua_State* L)
{ {
@ -152,9 +302,9 @@ int channel_open(lua_State* L)
string channelName(name, namelen); string channelName(name, namelen);
{ {
unique_lock<mutex> ulk(gChannelNSLock); unique_lock<mutex> ulk(globalChannelNSLock);
auto iter = gChannelNS.find(channelName); auto iter = globalChannelNS.find(channelName);
if (iter == gChannelNS.end()) if (iter == globalChannelNS.end())
{ {
// not found. // not found.
lua_pushnil(L); lua_pushnil(L);
@ -172,7 +322,7 @@ int channel_open(lua_State* L)
else else
{ {
printf("channel %s expired.\n", iter->first.c_str()); printf("channel %s expired.\n", iter->first.c_str());
gChannelNS.erase(iter); globalChannelNS.erase(iter);
// found but expired. // found but expired.
lua_pushnil(L); lua_pushnil(L);
return 1; return 1;
@ -193,9 +343,9 @@ int channel_create(lua_State* L)
} }
{ {
unique_lock<mutex> ulk(gChannelNSLock); unique_lock<mutex> ulk(globalChannelNSLock);
auto iter = gChannelNS.find(channelName); auto iter = globalChannelNS.find(channelName);
if (iter != gChannelNS.end()) if (iter != globalChannelNS.end())
{ {
auto spChan = iter->second.lock(); auto spChan = iter->second.lock();
if (spChan) if (spChan)
@ -211,7 +361,7 @@ int channel_create(lua_State* L)
{ {
// found but expired. // found but expired.
printf("channel %s expired.\n", iter->first.c_str()); printf("channel %s expired.\n", iter->first.c_str());
gChannelNS.erase(iter); globalChannelNS.erase(iter);
} }
} }
@ -219,7 +369,7 @@ int channel_create(lua_State* L)
shared_ptr<Channel> spChan(new Channel(cap)); shared_ptr<Channel> spChan(new Channel(cap));
put_channel(L, spChan); put_channel(L, spChan);
lua_pushboolean(L, 1); lua_pushboolean(L, 1);
gChannelNS.emplace(channelName, spChan); globalChannelNS.emplace(channelName, spChan);
printf("channel %p(%s) created.\n", spChan.get(), channelName.c_str()); printf("channel %p(%s) created.\n", spChan.get(), channelName.c_str());
@ -229,14 +379,14 @@ int channel_create(lua_State* L)
int channel_collect(lua_State* L) int channel_collect(lua_State* L)
{ {
unique_lock<mutex> ulk(gChannelNSLock); unique_lock<mutex> ulk(globalChannelNSLock);
for (auto iter = gChannelNS.begin(); iter != gChannelNS.end(); ) for (auto iter = globalChannelNS.begin(); iter != globalChannelNS.end(); )
{ {
auto sp = iter->second.lock(); auto sp = iter->second.lock();
if (!sp) if (!sp)
{ {
// expired. // expired.
iter = gChannelNS.erase(iter); iter = globalChannelNS.erase(iter);
} }
else else
{ {

View File

@ -3,6 +3,7 @@
/* /*
class Font class Font
constructor(filename: string, size: int) constructor(filename: string, size: int)
constructor(data: string, size: int, flag: int) flag is not used currently.
renderText(str: string, r: int, g: int, b: int, a: int): Surface renderText(str: string, r: int, g: int, b: int, a: int): Surface
renderText(rnd: Renderer, str: string, r: int, g: int, b: int, a: int): Texture renderText(rnd: Renderer, str: string, r: int, g: int, b: int, a: int): Texture
*/ */
@ -104,13 +105,32 @@ int font_renderutf8(lua_State* L)
int font_new(lua_State* L) int font_new(lua_State* L)
{ {
const char* filename = luaL_checkstring(L, 1); TTF_Font* font;
int fontsize = luaL_checkinteger(L, 2); if (lua_gettop(L) >= 3)
TTF_Font* font = TTF_OpenFont(filename, fontsize);
if (!font)
{ {
return TTFError(L, TTF_OpenFont); size_t datalen;
const char* data = luaL_checklstring(L, 1, &datalen);
int fontsize = luaL_checkinteger(L, 2);
int flag = luaL_checkinteger(L, 3);
SDL_RWops* src = SDL_RWFromConstMem(data, datalen);
font = TTF_OpenFontRW(src, 0, fontsize);
SDL_RWclose(src);
if (!font)
{
return TTFError(L, TTF_OpenFontRW);
}
} }
else
{
const char* filename = luaL_checkstring(L, 1);
int fontsize = luaL_checkinteger(L, 2);
font = TTF_OpenFont(filename, fontsize);
if (!font)
{
return TTFError(L, TTF_OpenFont);
}
}
lua_newpointer(L, font); lua_newpointer(L, font);
if (luaL_newmetatable(L, "LuaEngineFont")) if (luaL_newmetatable(L, "LuaEngineFont"))
{ {

View File

@ -3,11 +3,19 @@
/* /*
module Music module Music
load(filename: string): MusicMedia load(filename: string): MusicMedia
loadmem(data: string): MusicMedia
loadChunk(filename: string): ChunkMedia loadChunk(filename: string): ChunkMedia
loadChunkmem(data: string): ChunkMedia
play(music: MusicMedia, [loops: int]) Loops default to 1 time play(music: MusicMedia, [loops: int]) Loops default to 1 time
playChunk(chunk: ChunkMedia, channel: int, [loops: int]) Loops default to 1 time. playChunk(chunk: ChunkMedia, channel: int, [loops: int]) Loops default to 1 time.
fadeIn(music: MusicMedia, ms: int, [loops: int]) Loops default to 1.
fadeInPos(music: MusicMedia, ms: int, pos: number, [loops: int]) Loops default to 1.
fadeOut(ms: int)
pause() pause()
resume() resume()
rewind()
setPos(pos: number)
stop()
*/ */
int media_close(lua_State* L) int media_close(lua_State* L)
@ -119,6 +127,49 @@ int music_play(lua_State* L)
return 0; return 0;
} }
int music_fadein(lua_State* L)
{
auto music = lua_checkpointer<Mix_Music>(L, 1, "LuaEngineMusicMedia");
int ms = luaL_checkinteger(L, 2);
int loops = 0;
if (!lua_isnone(L, 3))
{
loops = luaL_checkinteger(L, 3);
}
if (Mix_FadeInMusic(music, loops, ms) != 0)
{
return MixError(L, Mix_FadeInMusic);
}
return 0;
}
int music_fadeinpos(lua_State* L)
{
auto music = lua_checkpointer<Mix_Music>(L, 1, "LuaEngineMusicMedia");
int ms = luaL_checkinteger(L, 2);
double pos = luaL_checknumber(L, 3);
int loops = 0;
if (!lua_isnone(L, 4))
{
loops = luaL_checkinteger(L, 4);
}
if (Mix_FadeInMusicPos(music, loops, ms, pos) != 0)
{
return MixError(L, Mix_FadeInMusicPos);
}
return 0;
}
int music_fadeout(lua_State* L)
{
int ms = luaL_checkinteger(L, 1);
if (Mix_FadeOutMusic(ms) != 0)
{
return MixError(L, Mix_FadeOutMusic);
}
return 0;
}
int music_playchunk(lua_State* L) int music_playchunk(lua_State* L)
{ {
auto chunk = lua_checkpointer<Mix_Chunk>(L, 1, "LuaEngineChunkMedia"); auto chunk = lua_checkpointer<Mix_Chunk>(L, 1, "LuaEngineChunkMedia");
@ -149,6 +200,28 @@ int music_resume(lua_State* L)
return 0; return 0;
} }
int music_stop(lua_State* L)
{
Mix_HaltMusic();
return 0;
}
int music_rewind(lua_State* L)
{
Mix_RewindMusic();
return 0;
}
int music_setpos(lua_State* L)
{
double pos = luaL_checknumber(L, 1);
if (Mix_SetMusicPosition(pos) != 0)
{
return MixError(L, Mix_SetMusicPosition);
}
return 0;
}
void InitMusic(lua_State* L) void InitMusic(lua_State* L)
{ {
lua_getglobal(L, "package"); lua_getglobal(L, "package");
@ -159,9 +232,14 @@ void InitMusic(lua_State* L)
lua_setfield_function(L, "loadChunk", music_loadchunk); lua_setfield_function(L, "loadChunk", music_loadchunk);
lua_setfield_function(L, "loadChunkmem", music_loadchunkmem); lua_setfield_function(L, "loadChunkmem", music_loadchunkmem);
lua_setfield_function(L, "play", music_play); lua_setfield_function(L, "play", music_play);
lua_setfield_function(L, "fadeIn", music_fadein);
lua_setfield_function(L, "fadeInPos", music_fadeinpos);
lua_setfield_function(L, "playChunk", music_playchunk); lua_setfield_function(L, "playChunk", music_playchunk);
lua_setfield_function(L, "fadeOut", music_fadeout);
lua_setfield_function(L, "pause", music_pause); lua_setfield_function(L, "pause", music_pause);
lua_setfield_function(L, "resume", music_resume); lua_setfield_function(L, "resume", music_resume);
lua_setfield_function(L, "stop", music_stop);
lua_setfield_function(L, "setPos", music_setpos);
lua_setfield(L, -2, "Music"); lua_setfield(L, -2, "Music");
lua_pop(L, 2); lua_pop(L, 2);
} }

View File

@ -5,9 +5,14 @@
class Renderer class Renderer
constructor(wnd: Window) constructor(wnd: Window)
load(filename: string): Texture load(filename: string): Texture
loadmem(data: string): Texture
render(surf: Surface): Texture
setTarget(text: Texture)
clear()
update()
copy(text: Texture, sx: int, sy: int, sw: int, sh: int, dx: int, dy: int, dw: int, dh: int) copy(text: Texture, sx: int, sy: int, sw: int, sh: int, dx: int, dy: int, dw: int, dh: int)
copyTo(text: Texture, x: int, y: int, [w: int, h: int]) copyTo(text: Texture, dx: int, dy: int, [dw: int, dh: int])
copyFill(text: Texture, x: int, y: int, w: int, h: int) copyFill(text: Texture, sx: int, sy: int, sw: int, sh: int)
copyFullFill(text: Texture) copyFullFill(text: Texture)
drawPoint(x: int, y: int) drawPoint(x: int, y: int)
drawLine(x1: int, y1: int, x2: int, y2: int) drawLine(x1: int, y1: int, x2: int, y2: int)
@ -55,6 +60,41 @@ int render_loadmem(lua_State* L)
return 1; return 1;
} }
int render_render(lua_State* L)
{
auto rnd = lua_checkpointer<SDL_Renderer>(L, 1, "LuaEngineRenderer");
auto surf = lua_checkpointer<SDL_Surface>(L, 2, "LuaEngineSurface");
SDL_Texture* text = SDL_CreateTextureFromSurface(rnd, surf);
if (!text)
{
return SDLError(L, SDL_CreateTextureFromSurface);
}
put_texture(L, text);
return 1;
}
int render_settarget(lua_State* L)
{
auto rnd = lua_checkpointer<SDL_Renderer>(L, 1, "LuaEngineRenderer");
if (lua_gettop(L) == 1)
{
if (SDL_SetRenderTarget(rnd, NULL) != 0)
{
return SDLError(L, SDL_SetRenderTarget);
}
return 0;
}
else
{
auto text = lua_checkpointer<SDL_Texture>(L, 2, "LuaEngineTexture");
if (SDL_SetRenderTarget(rnd, text) != 0)
{
return SDLError(L, SDL_SetRenderTarget);
}
return 0;
}
}
int render_clear(lua_State* L) int render_clear(lua_State* L)
{ {
auto rnd = lua_checkpointer<SDL_Renderer>(L, 1, "LuaEngineRenderer"); auto rnd = lua_checkpointer<SDL_Renderer>(L, 1, "LuaEngineRenderer");
@ -389,6 +429,8 @@ int render_new(lua_State* L)
lua_newtable(L); lua_newtable(L);
lua_setfield_function(L, "load", render_load); lua_setfield_function(L, "load", render_load);
lua_setfield_function(L, "loadmem", render_loadmem); lua_setfield_function(L, "loadmem", render_loadmem);
lua_setfield_function(L, "render", render_render);
lua_setfield_function(L, "setTarget", render_settarget);
lua_setfield_function(L, "clear", render_clear); lua_setfield_function(L, "clear", render_clear);
lua_setfield_function(L, "update", render_update); lua_setfield_function(L, "update", render_update);
lua_setfield_function(L, "copy", render_copy); lua_setfield_function(L, "copy", render_copy);