Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Polish the lua data model. #150

Merged
merged 4 commits into from
Dec 2, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions Samples/luainvaders/data/options.rml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ if Options.datamodel == nil then
Options.datamodel = rmlui.contexts["main"]:OpenDataModel("options", {
graphics = 'ok',
options_changed = false,
audios = {
{id="reverb", label="Reverb", checked=true},
{id="3d", label="3D Spatialisation"},
}
})
end

Expand Down Expand Up @@ -119,8 +123,9 @@ end
<p data-if="graphics == 'bad'">Are you sure about this? Bad graphics are just plain <em>bad.</em></p>
<p>
Audio:<br />
<input id="reverb" type="checkbox" name="reverb" checked /> Reverb<br />
<input id="3d" type="checkbox" name="3d" /> 3D Spatialisation
<span data-for="audio : audios">
<input data-attr-id="audio.id" type="checkbox" data-attr-name="audio.id" data-attrif-checked="audio.checked" /> {{audio.label}}<br />
</span>
</p>
</div>
<input type="submit" name="button" value="accept" data-attrif-disabled="!options_changed">Accept</input>
Expand Down
305 changes: 245 additions & 60 deletions Source/Lua/LuaDataModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,40 +37,232 @@
namespace Rml {
namespace Lua {

namespace luabind {
#if LUA_VERSION_NUM < 503
static void lua_reverse(lua_State* L, int a, int b) {
for (; a < b; ++a, --b) {
lua_pushvalue(L, a);
lua_pushvalue(L, b);
lua_replace(L, a);
lua_replace(L, b);
}
}
void lua_rotate(lua_State* L, int idx, int n) {
int n_elems = 0;
idx = lua_absindex(L, idx);
n_elems = lua_gettop(L) - idx + 1;
if (n < 0) {
n += n_elems;
}
if (n > 0 && n < n_elems) {
luaL_checkstack(L, 2, "not enough stack slots available");
n = n_elems - n;
lua_reverse(L, idx, idx + n - 1);
lua_reverse(L, idx + n, idx + n_elems - 1);
lua_reverse(L, idx, idx + n_elems - 1);
}
}
#endif
typedef std::function<void(void)> call_t;
inline int errhandler(lua_State* L) {
const char* msg = lua_tostring(L, 1);
if (msg == NULL) {
if (luaL_callmeta(L, 1, "__tostring") && lua_type(L, -1) == LUA_TSTRING)
return 1;
else
msg = lua_pushfstring(L, "(error object is a %s value)", luaL_typename(L, 1));
}
luaL_traceback(L, L, msg, 1);
return 1;
}
inline void errfunc(const char* msg) {
Log::Message(Log::LT_WARNING, "%s", msg);
}
inline int function_call(lua_State* L) {
call_t& f = *(call_t*)lua_touserdata(L, 1);
f();
return 0;
}
inline bool invoke(lua_State* L, call_t f, int argn = 0) {
if (!lua_checkstack(L, 3)) {
errfunc("stack overflow");
lua_pop(L, argn);
return false;
}
lua_pushcfunction(L, errhandler);
lua_pushcfunction(L, function_call);
lua_pushlightuserdata(L, &f);
lua_rotate(L, -argn - 3, 3);
if (lua_pcall(L, 1 + argn, 0, lua_gettop(L) - argn - 2) != LUA_OK) {
errfunc(lua_tostring(L, -1));
lua_pop(L, 2);
return false;
}
lua_pop(L, 1);
return true;
}
}

class LuaScalarDef;
class LuaTableDef;

struct LuaDataModel {
DataModelConstructor constructor;
DataModelHandle handle;
lua_State *dataL;
LuaScalarDef *scalarDef;
LuaTableDef *tableDef;
int top;
};

class LuaTableDef : public VariableDefinition {
public:
LuaTableDef(const struct LuaDataModel* model);
virtual bool Get(void* ptr, Variant& variant);
virtual bool Set(void* ptr, const Variant& variant);
virtual int Size(void* ptr);
virtual DataVariable Child(void* ptr, const DataAddressEntry& address);
protected:
const struct LuaDataModel* model;
};

class LuaScalarDef final : public VariableDefinition {
class LuaScalarDef final : public LuaTableDef {
public:
LuaScalarDef (const struct LuaDataModel *model) :
VariableDefinition(DataVariableType::Scalar), model(model) {}
private:
bool Get(void* ptr, Variant& variant) override {
lua_State *L = model->dataL;
if (!L)
return false;
int id = int((intptr_t)ptr);
GetVariant(L, id, &variant);
return true;
LuaScalarDef(const struct LuaDataModel* model);
virtual DataVariable Child(void* ptr, const DataAddressEntry& address);
};

LuaTableDef::LuaTableDef(const struct LuaDataModel *model)
: VariableDefinition(DataVariableType::Scalar)
, model(model)
{}

bool LuaTableDef::Get(void* ptr, Variant& variant) {
lua_State *L = model->dataL;
if (!L)
return false;
int id = (int)(intptr_t)ptr;
GetVariant(L, id, &variant);
return true;
}

bool LuaTableDef::Set(void* ptr, const Variant& variant) {
int id = (int)(intptr_t)ptr;
lua_State *L = model->dataL;
if (!L)
return false;
PushVariant(L, &variant);
lua_replace(L, id);
return true;
}


static int
lLuaTableDefSize(lua_State* L) {
lua_pushinteger(L, luaL_len(L, 1));
return 1;
}

static int
lLuaTableDefChild(lua_State* L) {
lua_gettable(L, 1);
return 1;
}

int LuaTableDef::Size(void* ptr) {
lua_State* L = model->dataL;
if (!L)
return 0;
int id = (int)(intptr_t)ptr;
if (lua_type(L, id) != LUA_TTABLE) {
return 0;
}
bool Set(void* ptr, const Rml::Variant& variant) override {
int id = int((intptr_t)ptr);
lua_State *L = model->dataL;
if (!L)
return false;
PushVariant(L, &variant);
lua_replace(L, id);
return true;
if (!lua_checkstack(L, 4)) {
return 0;
}
lua_pushcfunction(L, lLuaTableDefSize);
lua_pushvalue(L, id);
if (LUA_OK != lua_pcall(L, 1, 1, 0)) {
lua_pop(L, 1);
return 0;
}
int size = (int)lua_tointeger(L, -1);
lua_pop(L, 1);
return size;
}

const struct LuaDataModel *model;
};
DataVariable LuaTableDef::Child(void* ptr, const DataAddressEntry& address) {
lua_State* L = model->dataL;
if (!L)
return DataVariable{};
int id = (int)(intptr_t)ptr;
if (lua_type(L, id) != LUA_TTABLE) {
return DataVariable{};
}
if (!lua_checkstack(L, 4)) {
return DataVariable{};
}
lua_pushcfunction(L, lLuaTableDefChild);
lua_pushvalue(L, id);
if (address.index == -1) {
lua_pushlstring(L, address.name.data(), address.name.size());
}
else {
lua_pushinteger(L, (lua_Integer)address.index + 1);
}
if (LUA_OK != lua_pcall(L, 2, 1, 0)) {
lua_pop(L, 1);
return DataVariable{};
}
return DataVariable(model->tableDef, (void*)(intptr_t)lua_gettop(L));
}

LuaScalarDef::LuaScalarDef(const struct LuaDataModel* model)
: LuaTableDef(model)
{}

DataVariable LuaScalarDef::Child(void* ptr, const DataAddressEntry& address) {
lua_State* L = model->dataL;
if (!L)
return DataVariable{};
lua_settop(L, model->top);
return LuaTableDef::Child(ptr, address);
}

static void
BindVariable(struct LuaDataModel* D, lua_State* L) {
lua_State* dataL = D->dataL;
if (!lua_checkstack(dataL, 4)) {
luaL_error(L, "Memory Error");
}
int id = lua_gettop(dataL) + 1;
D->top = id;
// L top : key value
lua_xmove(L, dataL, 1); // move value to dataL with index(id)
lua_pushvalue(L, -1); // dup key
lua_xmove(L, dataL, 1);
lua_pushinteger(dataL, id);
lua_rawset(dataL, 1);
const char* key = lua_tostring(L, -1);
if (lua_type(dataL, D->top) == LUA_TFUNCTION) {
D->constructor.BindEventCallback(key, [=](DataModelHandle, Event& event, const VariantList& varlist) {
lua_pushvalue(dataL, id);
lua_xmove(dataL, L, 1);
luabind::invoke(L, [&](){
LuaType<Event>::push(L,&event,false);
for (auto const& variant : varlist) {
PushVariant(L, &variant);
}
lua_call(L, (int)varlist.size() + 1, 0);
}, 1);
});
}
else {
D->constructor.BindCustomDataVariable(key,
DataVariable(D->scalarDef, (void*)(intptr_t)id)
);
}
}

static int
getId(lua_State *L, lua_State *dataL) {
Expand Down Expand Up @@ -102,51 +294,32 @@ static int
lDataModelSet(lua_State *L) {
struct LuaDataModel *D = (struct LuaDataModel *)lua_touserdata(L, 1);
lua_State *dataL = D->dataL;
if (dataL == nullptr)
luaL_error(L, "DataModel closed");
int id = getId(L, dataL);
lua_xmove(L, dataL, 1);
lua_replace(dataL, id);
D->handle.DirtyVariable(lua_tostring(L, 2));
return 0;
}
if (dataL == NULL)
luaL_error(L, "DataModel released");
lua_settop(dataL, D->top);

// Construct a lua sub thread for LuaDataModel
// stack 1 : { name(string) -> id(integer) }
// stack 2- : values
// For example : build from { str = "Hello", x = 0 }
// 1: { str = 2 , x = 3 }
// 2: "Hello"
// 3: 0
static lua_State *
InitDataModelFromTable(lua_State *L, int index, Rml::DataModelConstructor &ctor, class LuaScalarDef *def) {
lua_State *dataL = lua_newthread(L);
lua_newtable(dataL);
intptr_t id = 2;
lua_pushnil(L);
while (lua_next(L, index) != 0) {
if (!lua_checkstack(dataL, 4)) {
luaL_error(L, "Memory Error");
}
// L top : key value
lua_xmove(L, dataL, 1); // move value to dataL with index(id)
lua_pushvalue(L, -1); // dup key
lua_pushvalue(L, 2);
lua_xmove(L, dataL, 1);
lua_rawget(dataL, 1);
if (lua_type(dataL, -1) == LUA_TNUMBER) {
int id = (int)lua_tointeger(dataL, -1);
lua_pop(dataL, 1);
lua_xmove(L, dataL, 1);
lua_pushinteger(dataL, id);
lua_rawset(dataL, 1);
const char *key = lua_tostring(L, -1);
ctor.BindCustomDataVariable(key, Rml::DataVariable(def, (void *)id));
++id;
lua_replace(dataL, id);
D->handle.DirtyVariable(lua_tostring(L, 2));
return 0;
}
return dataL;
lua_pop(dataL, 1);
BindVariable(D, L);
return 0;
}

bool
OpenLuaDataModel(lua_State *L, Rml::Context *context, int name_index, int table_index) {
Rml::String name = luaL_checkstring(L, name_index);
OpenLuaDataModel(lua_State *L, Context *context, int name_index, int table_index) {
String name = luaL_checkstring(L, name_index);
luaL_checktype(L, table_index, LUA_TTABLE);

Rml::DataModelConstructor constructor = context->CreateDataModel(name);
DataModelConstructor constructor = context->CreateDataModel(name);
if (!constructor) {
constructor = context->GetDataModel(name);
if (!constructor) {
Expand All @@ -157,11 +330,20 @@ OpenLuaDataModel(lua_State *L, Rml::Context *context, int name_index, int table_
struct LuaDataModel *D = (struct LuaDataModel *)lua_newuserdata(L, sizeof(*D));
D->dataL = nullptr;
D->scalarDef = nullptr;
D->tableDef = nullptr;
D->constructor = constructor;
D->handle = constructor.GetModelHandle();

D->scalarDef = new LuaScalarDef(D);
D->dataL = InitDataModelFromTable(L, table_index, constructor, D->scalarDef);
lua_setuservalue(L, -2); // set D->dataL into uservalue of D
D->tableDef = new LuaTableDef(D);
D->dataL = lua_newthread(L);
D->top = 1;
lua_newtable(D->dataL);
lua_pushnil(L);
while (lua_next(L, table_index) != 0) {
BindVariable(D, L);
}
lua_setuservalue(L, -2);

if (luaL_newmetatable(L, RMLDATAMODEL)) {
luaL_Reg l[] = {
Expand All @@ -185,8 +367,11 @@ CloseLuaDataModel(lua_State *L) {
luaL_checkudata(L, -1, RMLDATAMODEL);
struct LuaDataModel *D = (struct LuaDataModel *)lua_touserdata(L, -1);
D->dataL = nullptr;
D->top = 0;
delete D->scalarDef;
D->scalarDef = nullptr;
delete D->tableDef;
D->tableDef = nullptr;
lua_pushnil(L);
lua_setuservalue(L, -2);
}
Expand Down