LuaDataModel.cpp 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. /*
  2. * This source file is part of RmlUi, the HTML/CSS Interface Middleware
  3. *
  4. * For the latest information, see http://github.com/mikke89/RmlUi
  5. *
  6. * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
  7. * Copyright (c) 2019 The RmlUi Team, and contributors
  8. *
  9. * Permission is hereby granted, free of charge, to any person obtaining a copy
  10. * of this software and associated documentation files (the "Software"), to deal
  11. * in the Software without restriction, including without limitation the rights
  12. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. * copies of the Software, and to permit persons to whom the Software is
  14. * furnished to do so, subject to the following conditions:
  15. *
  16. * The above copyright notice and this permission notice shall be included in
  17. * all copies or substantial portions of the Software.
  18. *
  19. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. * THE SOFTWARE.
  26. *
  27. */
  28. #include "LuaDataModel.h"
  29. #include <RmlUi/Lua/Utilities.h>
  30. #include <RmlUi/Core/DataVariable.h>
  31. #include <RmlUi/Core/Context.h>
  32. #include <RmlUi/Core/DataModelHandle.h>
  33. #define RMLDATAMODEL "RMLDATAMODEL"
  34. namespace Rml {
  35. namespace Lua {
  36. namespace luabind {
  37. #if LUA_VERSION_NUM < 503
  38. static void lua_reverse(lua_State* L, int a, int b) {
  39. for (; a < b; ++a, --b) {
  40. lua_pushvalue(L, a);
  41. lua_pushvalue(L, b);
  42. lua_replace(L, a);
  43. lua_replace(L, b);
  44. }
  45. }
  46. void lua_rotate(lua_State* L, int idx, int n) {
  47. int n_elems = 0;
  48. idx = lua_absindex(L, idx);
  49. n_elems = lua_gettop(L) - idx + 1;
  50. if (n < 0) {
  51. n += n_elems;
  52. }
  53. if (n > 0 && n < n_elems) {
  54. luaL_checkstack(L, 2, "not enough stack slots available");
  55. n = n_elems - n;
  56. lua_reverse(L, idx, idx + n - 1);
  57. lua_reverse(L, idx + n, idx + n_elems - 1);
  58. lua_reverse(L, idx, idx + n_elems - 1);
  59. }
  60. }
  61. #endif
  62. using call_t = Rml::Function<void(void)>;
  63. inline int errhandler(lua_State* L) {
  64. const char* msg = lua_tostring(L, 1);
  65. if (msg == NULL) {
  66. if (luaL_callmeta(L, 1, "__tostring") && lua_type(L, -1) == LUA_TSTRING)
  67. return 1;
  68. else
  69. msg = lua_pushfstring(L, "(error object is a %s value)", luaL_typename(L, 1));
  70. }
  71. luaL_traceback(L, L, msg, 1);
  72. return 1;
  73. }
  74. inline void errfunc(const char* msg) {
  75. Log::Message(Log::LT_WARNING, "%s", msg);
  76. }
  77. inline int function_call(lua_State* L) {
  78. call_t& f = *(call_t*)lua_touserdata(L, 1);
  79. f();
  80. return 0;
  81. }
  82. inline bool invoke(lua_State* L, call_t f, int argn = 0) {
  83. if (!lua_checkstack(L, 3)) {
  84. errfunc("stack overflow");
  85. lua_pop(L, argn);
  86. return false;
  87. }
  88. lua_pushcfunction(L, errhandler);
  89. lua_pushcfunction(L, function_call);
  90. lua_pushlightuserdata(L, &f);
  91. lua_rotate(L, -argn - 3, 3);
  92. if (lua_pcall(L, 1 + argn, 0, lua_gettop(L) - argn - 2) != LUA_OK) {
  93. errfunc(lua_tostring(L, -1));
  94. lua_pop(L, 2);
  95. return false;
  96. }
  97. lua_pop(L, 1);
  98. return true;
  99. }
  100. }
  101. class LuaScalarDef;
  102. class LuaTableDef;
  103. struct LuaDataModel {
  104. DataModelConstructor constructor;
  105. DataModelHandle handle;
  106. lua_State *dataL;
  107. LuaScalarDef *scalarDef;
  108. LuaTableDef *tableDef;
  109. int top;
  110. };
  111. class LuaTableDef : public VariableDefinition {
  112. public:
  113. LuaTableDef(const struct LuaDataModel* model);
  114. bool Get(void* ptr, Variant& variant) override;
  115. bool Set(void* ptr, const Variant& variant) override;
  116. int Size(void* ptr) override;
  117. DataVariable Child(void* ptr, const DataAddressEntry& address) override;
  118. protected:
  119. const struct LuaDataModel* model;
  120. };
  121. class LuaScalarDef final : public LuaTableDef {
  122. public:
  123. LuaScalarDef(const struct LuaDataModel* model);
  124. DataVariable Child(void* ptr, const DataAddressEntry& address) override;
  125. };
  126. LuaTableDef::LuaTableDef(const struct LuaDataModel *model)
  127. : VariableDefinition(DataVariableType::Scalar)
  128. , model(model)
  129. {}
  130. bool LuaTableDef::Get(void* ptr, Variant& variant) {
  131. lua_State *L = model->dataL;
  132. if (!L)
  133. return false;
  134. int id = (int)(intptr_t)ptr;
  135. GetVariant(L, id, &variant);
  136. return true;
  137. }
  138. bool LuaTableDef::Set(void* ptr, const Variant& variant) {
  139. int id = (int)(intptr_t)ptr;
  140. lua_State *L = model->dataL;
  141. if (!L)
  142. return false;
  143. PushVariant(L, &variant);
  144. lua_replace(L, id);
  145. return true;
  146. }
  147. static int
  148. lLuaTableDefSize(lua_State* L) {
  149. lua_pushinteger(L, luaL_len(L, 1));
  150. return 1;
  151. }
  152. static int
  153. lLuaTableDefChild(lua_State* L) {
  154. lua_gettable(L, 1);
  155. return 1;
  156. }
  157. int LuaTableDef::Size(void* ptr) {
  158. lua_State* L = model->dataL;
  159. if (!L)
  160. return 0;
  161. int id = (int)(intptr_t)ptr;
  162. if (lua_type(L, id) != LUA_TTABLE) {
  163. return 0;
  164. }
  165. if (!lua_checkstack(L, 4)) {
  166. return 0;
  167. }
  168. lua_pushcfunction(L, lLuaTableDefSize);
  169. lua_pushvalue(L, id);
  170. if (LUA_OK != lua_pcall(L, 1, 1, 0)) {
  171. lua_pop(L, 1);
  172. return 0;
  173. }
  174. int size = (int)lua_tointeger(L, -1);
  175. lua_pop(L, 1);
  176. return size;
  177. }
  178. DataVariable LuaTableDef::Child(void* ptr, const DataAddressEntry& address) {
  179. lua_State* L = model->dataL;
  180. if (!L)
  181. return DataVariable{};
  182. int id = (int)(intptr_t)ptr;
  183. if (lua_type(L, id) != LUA_TTABLE) {
  184. return DataVariable{};
  185. }
  186. if (!lua_checkstack(L, 4)) {
  187. return DataVariable{};
  188. }
  189. lua_pushcfunction(L, lLuaTableDefChild);
  190. lua_pushvalue(L, id);
  191. if (address.index == -1) {
  192. lua_pushlstring(L, address.name.data(), address.name.size());
  193. }
  194. else {
  195. lua_pushinteger(L, (lua_Integer)address.index + 1);
  196. }
  197. if (LUA_OK != lua_pcall(L, 2, 1, 0)) {
  198. lua_pop(L, 1);
  199. return DataVariable{};
  200. }
  201. return DataVariable(model->tableDef, (void*)(intptr_t)lua_gettop(L));
  202. }
  203. LuaScalarDef::LuaScalarDef(const struct LuaDataModel* model)
  204. : LuaTableDef(model)
  205. {}
  206. DataVariable LuaScalarDef::Child(void* ptr, const DataAddressEntry& address) {
  207. lua_State* L = model->dataL;
  208. if (!L)
  209. return DataVariable{};
  210. lua_settop(L, model->top);
  211. return LuaTableDef::Child(ptr, address);
  212. }
  213. static void
  214. BindVariable(struct LuaDataModel* D, lua_State* L) {
  215. lua_State* dataL = D->dataL;
  216. if (!lua_checkstack(dataL, 4)) {
  217. luaL_error(L, "Memory Error");
  218. }
  219. int id = lua_gettop(dataL) + 1;
  220. D->top = id;
  221. // L top : key value
  222. lua_xmove(L, dataL, 1); // move value to dataL with index(id)
  223. lua_pushvalue(L, -1); // dup key
  224. lua_xmove(L, dataL, 1);
  225. lua_pushinteger(dataL, id);
  226. lua_rawset(dataL, 1);
  227. const char* key = lua_tostring(L, -1);
  228. if (lua_type(dataL, D->top) == LUA_TFUNCTION) {
  229. D->constructor.BindEventCallback(key, [=](DataModelHandle, Event& event, const VariantList& varlist) {
  230. lua_pushvalue(dataL, id);
  231. lua_xmove(dataL, L, 1);
  232. luabind::invoke(L, [&](){
  233. LuaType<Event>::push(L,&event,false);
  234. for (auto const& variant : varlist) {
  235. PushVariant(L, &variant);
  236. }
  237. lua_call(L, (int)varlist.size() + 1, 0);
  238. }, 1);
  239. });
  240. }
  241. else {
  242. D->constructor.BindCustomDataVariable(key,
  243. DataVariable(D->scalarDef, (void*)(intptr_t)id)
  244. );
  245. }
  246. }
  247. static int
  248. getId(lua_State *L, lua_State *dataL) {
  249. lua_pushvalue(dataL, 1);
  250. lua_xmove(dataL, L, 1);
  251. lua_pushvalue(L, 2);
  252. lua_rawget(L, -2);
  253. if (lua_type(L, -1) != LUA_TNUMBER) {
  254. luaL_error(L, "DataModel has no key : %s", lua_tostring(L, 2));
  255. }
  256. int id = (int)lua_tointeger(L, -1);
  257. lua_pop(L, 2);
  258. return id;
  259. }
  260. static int
  261. lDataModelGet(lua_State *L) {
  262. struct LuaDataModel *D = (struct LuaDataModel *)lua_touserdata(L, 1);
  263. lua_State *dataL = D->dataL;
  264. if (dataL == nullptr)
  265. luaL_error(L, "DataModel closed");
  266. int id = getId(L, dataL);
  267. lua_pushvalue(dataL, id);
  268. lua_xmove(dataL, L, 1);
  269. return 1;
  270. }
  271. static int
  272. lDataModelSet(lua_State *L) {
  273. struct LuaDataModel *D = (struct LuaDataModel *)lua_touserdata(L, 1);
  274. lua_State *dataL = D->dataL;
  275. if (dataL == NULL)
  276. luaL_error(L, "DataModel released");
  277. lua_settop(dataL, D->top);
  278. lua_pushvalue(L, 2);
  279. lua_xmove(L, dataL, 1);
  280. lua_rawget(dataL, 1);
  281. if (lua_type(dataL, -1) == LUA_TNUMBER) {
  282. int id = (int)lua_tointeger(dataL, -1);
  283. lua_pop(dataL, 1);
  284. lua_xmove(L, dataL, 1);
  285. lua_replace(dataL, id);
  286. D->handle.DirtyVariable(lua_tostring(L, 2));
  287. return 0;
  288. }
  289. lua_pop(dataL, 1);
  290. BindVariable(D, L);
  291. return 0;
  292. }
  293. bool
  294. OpenLuaDataModel(lua_State *L, Context *context, int name_index, int table_index) {
  295. String name = luaL_checkstring(L, name_index);
  296. luaL_checktype(L, table_index, LUA_TTABLE);
  297. DataModelConstructor constructor = context->CreateDataModel(name);
  298. if (!constructor) {
  299. constructor = context->GetDataModel(name);
  300. if (!constructor) {
  301. return false;
  302. }
  303. }
  304. struct LuaDataModel *D = (struct LuaDataModel *)lua_newuserdata(L, sizeof(*D));
  305. D->dataL = nullptr;
  306. D->scalarDef = nullptr;
  307. D->tableDef = nullptr;
  308. D->constructor = constructor;
  309. D->handle = constructor.GetModelHandle();
  310. D->scalarDef = new LuaScalarDef(D);
  311. D->tableDef = new LuaTableDef(D);
  312. D->dataL = lua_newthread(L);
  313. D->top = 1;
  314. lua_newtable(D->dataL);
  315. lua_pushnil(L);
  316. while (lua_next(L, table_index) != 0) {
  317. BindVariable(D, L);
  318. }
  319. lua_setuservalue(L, -2);
  320. if (luaL_newmetatable(L, RMLDATAMODEL)) {
  321. luaL_Reg l[] = {
  322. { "__index", lDataModelGet },
  323. { "__newindex", lDataModelSet },
  324. { nullptr, nullptr },
  325. };
  326. luaL_setfuncs(L, l, 0);
  327. }
  328. lua_setmetatable(L, -2);
  329. return true;
  330. }
  331. // If you create all the Data Models from lua, you can store these LuaDataModel objects in a table,
  332. // and call CloseLuaDataModel for each after Context released.
  333. // We don't put it in __gc, becuase LuaDataModel can be free before DataModel if you are not careful.
  334. // scalarDef will free by CloseLuaDataModel, but DataModel need it.
  335. void
  336. CloseLuaDataModel(lua_State *L) {
  337. luaL_checkudata(L, -1, RMLDATAMODEL);
  338. struct LuaDataModel *D = (struct LuaDataModel *)lua_touserdata(L, -1);
  339. D->dataL = nullptr;
  340. D->top = 0;
  341. delete D->scalarDef;
  342. D->scalarDef = nullptr;
  343. delete D->tableDef;
  344. D->tableDef = nullptr;
  345. lua_pushnil(L);
  346. lua_setuservalue(L, -2);
  347. }
  348. } // namespace Lua
  349. } // namespace Rml