diff --git a/Channel.cpp b/Channel.cpp index 8899753..0942839 100644 --- a/Channel.cpp +++ b/Channel.cpp @@ -7,16 +7,33 @@ #include 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> 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(L, 1, "LuaChannel"); + int type; string value; - printf("Call get on LuaChannel %p\n", c); - { unique_lock 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(L, 1, "LuaChannel"); + int ms = luaL_checkinteger(L, 2); + + bool success; + int type; + string value; + + { + unique_lock 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(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 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(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 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(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 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& spChan) { @@ -136,14 +283,17 @@ void put_channel(lua_State* L, const shared_ptr& 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> gChannelNS; +mutex globalChannelNSLock; +map> globalChannelNS; int channel_open(lua_State* L) { @@ -152,9 +302,9 @@ int channel_open(lua_State* L) string channelName(name, namelen); { - unique_lock ulk(gChannelNSLock); - auto iter = gChannelNS.find(channelName); - if (iter == gChannelNS.end()) + unique_lock 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 ulk(gChannelNSLock); - auto iter = gChannelNS.find(channelName); - if (iter != gChannelNS.end()) + unique_lock 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 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 ulk(gChannelNSLock); - for (auto iter = gChannelNS.begin(); iter != gChannelNS.end(); ) + unique_lock 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 { diff --git a/Font.cpp b/Font.cpp index 2a160eb..ae0e2b7 100644 --- a/Font.cpp +++ b/Font.cpp @@ -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")) { diff --git a/Music.cpp b/Music.cpp index 535fe7a..b746db1 100644 --- a/Music.cpp +++ b/Music.cpp @@ -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(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(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(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); } diff --git a/Renderer.cpp b/Renderer.cpp index 58ccada..89fb5b9 100644 --- a/Renderer.cpp +++ b/Renderer.cpp @@ -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(L, 1, "LuaEngineRenderer"); + auto surf = lua_checkpointer(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(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(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(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);