LuaEngine v0.5.1

Add api docs.
More support of loading resources from memory.
Minor fix.
master
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>
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
{
public:
int capacity;
bool closed;
queue<tuple<int, string>> bus;
mutex m;
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;
}
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
int channel_get(lua_State* L)
{
auto c = lua_checkblock<LuaChannel>(L, 1, "LuaChannel");
int type;
string value;
printf("Call get on LuaChannel %p\n", c);
{
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(); });
// Un-serialize from string to Lua object
@ -60,61 +126,71 @@ int channel_get(lua_State* L)
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 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.
int channel_put(lua_State* L)
{
auto c = lua_checkblock<LuaChannel>(L, 1, "LuaChannel");
int type = lua_type(L, 2);
int type;
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));
}
printf("Call put on LuaChannel %p\n", c);
{
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->bus.emplace(type, value);
c->sp->cond.notify_all();
@ -123,6 +199,77 @@ int channel_put(lua_State* L)
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.
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_setfield_function(L, "get", channel_get);
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_setmetatable(L, -2);
}
// Global Variables for managing LuaChannel openning and creating
mutex gChannelNSLock;
map<string, weak_ptr<Channel>> gChannelNS;
mutex globalChannelNSLock;
map<string, weak_ptr<Channel>> globalChannelNS;
int channel_open(lua_State* L)
{
@ -152,9 +302,9 @@ int channel_open(lua_State* L)
string channelName(name, namelen);
{
unique_lock<mutex> ulk(gChannelNSLock);
auto iter = gChannelNS.find(channelName);
if (iter == gChannelNS.end())
unique_lock<mutex> ulk(globalChannelNSLock);
auto iter = globalChannelNS.find(channelName);
if (iter == globalChannelNS.end())
{
// not found.
lua_pushnil(L);
@ -172,7 +322,7 @@ int channel_open(lua_State* L)
else
{
printf("channel %s expired.\n", iter->first.c_str());
gChannelNS.erase(iter);
globalChannelNS.erase(iter);
// found but expired.
lua_pushnil(L);
return 1;
@ -193,9 +343,9 @@ int channel_create(lua_State* L)
}
{
unique_lock<mutex> ulk(gChannelNSLock);
auto iter = gChannelNS.find(channelName);
if (iter != gChannelNS.end())
unique_lock<mutex> ulk(globalChannelNSLock);
auto iter = globalChannelNS.find(channelName);
if (iter != globalChannelNS.end())
{
auto spChan = iter->second.lock();
if (spChan)
@ -211,7 +361,7 @@ int channel_create(lua_State* L)
{
// found but expired.
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));
put_channel(L, spChan);
lua_pushboolean(L, 1);
gChannelNS.emplace(channelName, spChan);
globalChannelNS.emplace(channelName, spChan);
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)
{
unique_lock<mutex> ulk(gChannelNSLock);
for (auto iter = gChannelNS.begin(); iter != gChannelNS.end(); )
unique_lock<mutex> ulk(globalChannelNSLock);
for (auto iter = globalChannelNS.begin(); iter != globalChannelNS.end(); )
{
auto sp = iter->second.lock();
if (!sp)
{
// expired.
iter = gChannelNS.erase(iter);
iter = globalChannelNS.erase(iter);
}
else
{

View File

@ -3,6 +3,7 @@
/*
class Font
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(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)
{
const char* filename = luaL_checkstring(L, 1);
int fontsize = luaL_checkinteger(L, 2);
TTF_Font* font = TTF_OpenFont(filename, fontsize);
if (!font)
TTF_Font* font;
if (lua_gettop(L) >= 3)
{
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);
if (luaL_newmetatable(L, "LuaEngineFont"))
{

View File

@ -3,11 +3,19 @@
/*
module Music
load(filename: string): MusicMedia
loadmem(data: string): MusicMedia
loadChunk(filename: string): ChunkMedia
loadChunkmem(data: string): ChunkMedia
play(music: MusicMedia, [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()
resume()
rewind()
setPos(pos: number)
stop()
*/
int media_close(lua_State* L)
@ -119,6 +127,49 @@ int music_play(lua_State* L)
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)
{
auto chunk = lua_checkpointer<Mix_Chunk>(L, 1, "LuaEngineChunkMedia");
@ -149,6 +200,28 @@ int music_resume(lua_State* L)
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)
{
lua_getglobal(L, "package");
@ -159,9 +232,14 @@ void InitMusic(lua_State* L)
lua_setfield_function(L, "loadChunk", music_loadchunk);
lua_setfield_function(L, "loadChunkmem", music_loadchunkmem);
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, "fadeOut", music_fadeout);
lua_setfield_function(L, "pause", music_pause);
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_pop(L, 2);
}

View File

@ -5,9 +5,14 @@
class Renderer
constructor(wnd: Window)
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)
copyTo(text: Texture, x: int, y: int, [w: int, h: int])
copyFill(text: Texture, x: int, y: int, w: int, h: int)
copyTo(text: Texture, dx: int, dy: int, [dw: int, dh: int])
copyFill(text: Texture, sx: int, sy: int, sw: int, sh: int)
copyFullFill(text: Texture)
drawPoint(x: int, y: int)
drawLine(x1: int, y1: int, x2: int, y2: int)
@ -55,6 +60,41 @@ int render_loadmem(lua_State* L)
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)
{
auto rnd = lua_checkpointer<SDL_Renderer>(L, 1, "LuaEngineRenderer");
@ -389,6 +429,8 @@ int render_new(lua_State* L)
lua_newtable(L);
lua_setfield_function(L, "load", render_load);
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, "update", render_update);
lua_setfield_function(L, "copy", render_copy);