cross platform gui lib for lua
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

1135 lines
27 KiB

  1. /* lui.c
  2. *
  3. * lua binding to libui (https://github.com/andlabs/libui)
  4. *
  5. * Gunnar Zötl <gz@tset.de>, 2016
  6. * Released under MIT/X11 license. See file LICENSE for details.
  7. */
  8. const char *LUI_DEFAULT_WINDOW_TITLE = "(lui window)";
  9. const int LUI_DEFAULT_WINDOW_WIDTH = 600;
  10. const int LUI_DEFAULT_WINDOW_HEIGHT = 600;
  11. /*** Module
  12. * Name: lui
  13. */
  14. /*** Intro
  15. * lui is a cross platform gui library for lua, basically a souped up wrapper for libui (https://github.com/andlabs/libui).
  16. */
  17. #include <stdlib.h>
  18. #include <string.h>
  19. #include <time.h>
  20. #include "ui.h"
  21. #include "lua.h"
  22. #include "lauxlib.h"
  23. /* very raw approximations of some newer functions for lua 5.1 */
  24. #if LUA_VERSION_NUM == 501
  25. #define luaL_newlib(L,funcs) lua_newtable(L); luaL_register(L, NULL, funcs)
  26. /* ignores nup, so can only be used for functions without shared upvalues */
  27. #define luaL_setfuncs(L,funcs,x) luaL_register(L, NULL, funcs)
  28. /* only equivalent of the uservalue is a table */
  29. #define lua_setuservalue(L, idx) lua_setfenv(L, idx)
  30. /* on lua 5.1, an empty table is returned if no setfenv() call preceded this.
  31. * On later luas, nil is returned when the uservalue was not set. */
  32. #define lua_getuservalue(L, idx) lua_getfenv(L, idx)
  33. #define LUA_OK 0
  34. #endif
  35. #define NOT_IMPLEMENTED return luaL_error(L, "Not implemented!");
  36. static int lui_initialized = 0;
  37. /* utility stuff **********************************************************/
  38. /* lui functions need to check this */
  39. #define ensure_initialized() do { \
  40. if (!lui_initialized) { \
  41. luaL_error(L, "lui has not been initialized"); \
  42. } \
  43. } while (0)
  44. /* lui control methods need to check this */
  45. #define ensure_valid(lobj) do { \
  46. if (lobj->object == 0) { \
  47. luaL_error(L, "control has already been destroyed"); \
  48. } \
  49. } while (0)
  50. typedef struct {
  51. void *object;
  52. } lui_object;
  53. #define LUI_OBJECT_REGISTRY "lui_object_registry"
  54. #define lui_enumItem(N, V) lua_pushstring(L, (N)); lua_pushinteger(L, (V)); lua_settable(L, -3);
  55. #ifdef DEBUG
  56. #include <stdio.h>
  57. #define DEBUGMSG(...) do { fprintf(stderr, __VA_ARGS__); fputs("\n", stderr); } while (0)
  58. static const char* lui_debug_controlTostring(lua_State *L, int n)
  59. {
  60. static char buf[256];
  61. void *obj = lua_touserdata(L, n);
  62. const char *type;
  63. if (luaL_getmetafield (L, n, "__name") != LUA_TNIL) {
  64. type = lua_tostring(L, -1);
  65. lua_pop(L, 1);
  66. } else {
  67. type = "lui_control";
  68. }
  69. snprintf(buf, 256, "%s: %p", type, ((lui_object*)obj)->object);
  70. return buf;
  71. }
  72. #else
  73. #define DEBUGMSG(...)
  74. #define lui_debug_controlTostring(L, n) "[lui_debug_controlTostring]"
  75. #endif
  76. #define lui_aux_istable(L, n) (lua_type((L), (n)) == LUA_TTABLE)
  77. static int lui_aux_iscallable(lua_State *L, int pos)
  78. {
  79. int type = lua_type(L, pos);
  80. if (type == LUA_TFUNCTION) {
  81. return 1;
  82. }
  83. int ctype = luaL_getmetafield(L, pos, "__call");
  84. if (ctype != LUA_TNIL) {
  85. lua_pop(L, 1);
  86. }
  87. return ctype == LUA_TFUNCTION;
  88. }
  89. /* pos ==0 or pos ==1 inserts at start, pos > len or pos < 0 inserts at end */
  90. static void lui_aux_tinsert(lua_State *L, int tbl, int pos, int val)
  91. {
  92. int len = lua_rawlen(L, tbl);
  93. if (pos == 0) {
  94. pos = 1;
  95. }
  96. if (pos >= 1 && pos <= len) {
  97. for (int cnt = len; cnt >= pos; --cnt) {
  98. lua_rawgeti(L, tbl, cnt);
  99. lua_rawseti(L, tbl, cnt + 1);
  100. }
  101. } else if (pos < 0 || pos > len) {
  102. pos = len + 1;
  103. }
  104. lua_pushvalue(L, val);
  105. lua_rawseti(L, tbl, pos);
  106. }
  107. /* only indices between 1 and len are considered */
  108. static void lui_aux_tdelete(lua_State *L, int tbl, int pos)
  109. {
  110. int len = lua_rawlen(L, tbl);
  111. if (pos < 1 || pos > len) {
  112. return;
  113. }
  114. if (pos < len) {
  115. for (int cnt = pos; cnt < len; ++cnt) {
  116. lua_rawgeti(L, tbl, cnt + 1);
  117. lua_rawseti(L, tbl, cnt);
  118. }
  119. }
  120. lua_pushnil(L);
  121. lua_rawseti(L, tbl, len);
  122. }
  123. static int lui_aux_getUservalue(lua_State *L, int pos, const char *name)
  124. {
  125. if (lua_getuservalue(L, pos) != LUA_TNIL) {
  126. lua_getfield(L, -1, name);
  127. lua_copy(L, -1, -2);
  128. lua_pop(L, 1);
  129. }
  130. return lua_type(L, -1);
  131. }
  132. static int lui_aux_setUservalue(lua_State *L, int pos, const char *name, int vpos)
  133. {
  134. int top = lua_gettop(L);
  135. if (vpos < 0) {
  136. vpos += 1 + top;
  137. }
  138. lua_getuservalue(L, pos);
  139. lua_pushvalue(L, vpos);
  140. lua_setfield(L, -2, name);
  141. lua_pop(L, 1);
  142. return 0;
  143. }
  144. static int lui_aux_clearUservalue(lua_State *L, int pos, const char *name)
  145. {
  146. lua_getuservalue(L, pos);
  147. lua_pushnil(L);
  148. lua_setfield(L, -2, name);
  149. lua_pop(L, 1);
  150. return 0;
  151. }
  152. static int lui_aux_getRegistryTableField(lua_State *L, const char *tbl, const char *field)
  153. {
  154. lua_getfield(L, LUA_REGISTRYINDEX, tbl);
  155. lua_getfield(L, -1, field);
  156. lua_copy(L, -1, -2);
  157. lua_pop(L, 1);
  158. return lua_type(L, -1);
  159. }
  160. static int lui_aux_getNumberOrValue(lua_State *L, int pos, const char *map)
  161. {
  162. int isnum;
  163. int res = lua_tonumberx(L, pos, &isnum);
  164. if (!isnum) {
  165. const char *name = lua_tostring(L, pos);
  166. if (name) {
  167. if (lui_aux_getRegistryTableField(L, map, name) == LUA_TNUMBER) {
  168. res = lua_tonumber(L, -1);
  169. }
  170. lua_pop(L, 1);
  171. }
  172. }
  173. return res;
  174. }
  175. static const int lui_aux_pushNameOrValue(lua_State *L, int value, const char *map)
  176. {
  177. if (lua_getfield(L, LUA_REGISTRYINDEX, map) != LUA_TTABLE) {
  178. lua_pop(L, 1);
  179. lua_pushinteger(L, value);
  180. return 1;
  181. }
  182. int t = lua_gettop(L);
  183. lua_pushnil(L);
  184. while (lua_next(L, t) != 0) {
  185. if (lua_tonumber(L, -1) == value) {
  186. lua_copy(L, -2, t);
  187. lua_pop(L, 2);
  188. return 1;
  189. }
  190. lua_pop(L, 1);
  191. }
  192. lua_pop(L, 1);
  193. lua_pushinteger(L, value);
  194. return 1;
  195. }
  196. static const int lui_aux_setFieldsFromTable(lua_State *L, int obj, int tbl)
  197. {
  198. lua_pushnil(L);
  199. while (lua_next(L, tbl) != 0) {
  200. lua_pushvalue(L, -2);
  201. lua_pushvalue(L, -2);
  202. lua_settable(L, obj);
  203. lua_pop(L, 1);
  204. }
  205. return 0;
  206. }
  207. /* generic lui_object handling *********************************************/
  208. /* lui_toObject
  209. *
  210. * return a pointer to the userdata in position pos of the stack, cast to
  211. * a lui_object*
  212. */
  213. static lui_object* lui_toObject(lua_State *L, int pos)
  214. {
  215. lui_object *lobj = (lui_object*) lua_touserdata(L, pos);
  216. return lobj;
  217. }
  218. static void* lui_throwWrongObjectError(lua_State *L, int pos)
  219. {
  220. char buf[256];
  221. snprintf(buf, 256, "(expected lui object, got %s)", lua_typename(L, pos));
  222. luaL_argerror(L, pos, buf);
  223. return (void*)0;
  224. }
  225. /* lui_isObject
  226. * check if the data in position pos of the stack is a lui_object*, return
  227. * it if so, return NULL otherwise
  228. */
  229. static lui_object* lui_isObject(lua_State *L, int pos)
  230. {
  231. lui_object *lobj = (lui_object*) lua_touserdata(L, pos);
  232. if (!lobj || luaL_getmetafield(L, pos, "__name") == LUA_TNIL) {
  233. return 0;
  234. }
  235. const char *name = lua_tostring(L, -1);
  236. lua_pop(L, 1);
  237. if (strncmp(name, "lui_", 4) != 0) {
  238. return 0;
  239. }
  240. return lobj;
  241. }
  242. /* lui_checkObject
  243. *
  244. * check if the data in position pos of the stack is a lui_object*, return
  245. * it if so, fail otherwise
  246. */
  247. static lui_object* lui_checkObject(lua_State *L, int pos)
  248. {
  249. lui_object *lobj = (lui_object*) lui_isObject(L, pos);
  250. if (!lobj) {
  251. return lui_throwWrongObjectError(L, pos);
  252. }
  253. return lobj;
  254. }
  255. /* lui control registry handling
  256. */
  257. static int lui_registerObject(lua_State *L, int pos)
  258. {
  259. lui_object *lobj = lui_toObject(L, pos);
  260. lua_getfield(L, LUA_REGISTRYINDEX, LUI_OBJECT_REGISTRY);
  261. lua_pushlightuserdata(L, lobj->object);
  262. lua_pushvalue(L, pos);
  263. lua_settable(L, -3);
  264. lua_pop(L, 1);
  265. return 0;
  266. }
  267. static int lui_findObject(lua_State *L, const uiControl *control)
  268. {
  269. lua_getfield(L, LUA_REGISTRYINDEX, LUI_OBJECT_REGISTRY);
  270. lua_pushlightuserdata(L, (uiControl*)control);
  271. lua_gettable(L, -2);
  272. lua_copy(L, -1, -2);
  273. lua_pop(L, 1);
  274. return lua_type(L, -1);
  275. }
  276. static lui_object* lui_pushObject(lua_State *L, const char *type, int needuv)
  277. {
  278. ensure_initialized();
  279. lui_object *lobj = (lui_object*) lua_newuserdata(L, sizeof(lui_object));
  280. lobj->object = 0;
  281. luaL_getmetatable(L, type);
  282. lua_setmetatable(L, -2);
  283. if (needuv) {
  284. lua_newtable(L);
  285. lua_setuservalue(L, -2);
  286. }
  287. return lobj;
  288. }
  289. /* generic controls ******************************************************/
  290. /*** Object
  291. * Name: control
  292. * Generic UI control methods and properties. All controls have these. In
  293. * addition, all constructor methods can take a table of properties and
  294. * associated values as their last argument. If such a table is passed as
  295. * an argument, those properties are set after the control has been created.
  296. */
  297. /* lui_control__gc
  298. *
  299. * __gc metamethod lui controls
  300. */
  301. static int lui_control__gc(lua_State *L)
  302. {
  303. lui_object *lobj = lui_toObject(L, 1);
  304. if (lobj->object) {
  305. if (lui_aux_getUservalue(L, 1, "parent") != LUA_TNIL) {
  306. DEBUGMSG("lui_control__gc (%s), has parent", lui_debug_controlTostring(L, 1));
  307. lui_aux_clearUservalue(L, 1, "parent");
  308. lobj->object = 0;
  309. lua_pop(L, 1);
  310. return 0;
  311. }
  312. lua_pop(L, 1);
  313. if (lui_aux_getUservalue(L, 1, "child") != LUA_TNIL) {
  314. DEBUGMSG("lui_control__gc (%s), has child", lui_debug_controlTostring(L, 1));
  315. lui_object *cobj = lui_toObject(L, 2);
  316. if (cobj->object) {
  317. lui_aux_clearUservalue(L, 2, "parent");
  318. cobj->object = 0;
  319. }
  320. uiControlDestroy(lobj->object);
  321. lobj->object = 0;
  322. lua_pop(L, 1);
  323. return 0;
  324. }
  325. lua_pop(L, 1);
  326. if (lui_aux_getUservalue(L, 1, "children") != LUA_TNIL) {
  327. DEBUGMSG("lui_control__gc (%s), has children", lui_debug_controlTostring(L, 1));
  328. int len = lua_rawlen(L, 2);
  329. for (int i = 1; i < len; ++i) {
  330. if (lua_rawgeti(L, 2, i) != LUA_TNIL) {
  331. lui_object *cobj = lui_toObject(L, 3);
  332. if (cobj->object) {
  333. lui_aux_clearUservalue(L, 3, "parent");
  334. cobj->object = 0;
  335. }
  336. }
  337. lua_pop(L, 1);
  338. }
  339. uiControlDestroy(lobj->object);
  340. lobj->object = 0;
  341. lua_pop(L, 1);
  342. return 0;
  343. }
  344. lua_pop(L, 1);
  345. DEBUGMSG("lui_control__gc (%s)", lui_debug_controlTostring(L, 1));
  346. uiControlDestroy(lobj->object);
  347. lobj->object = 0;
  348. }
  349. return 0;
  350. }
  351. /* lui_control__toString
  352. *
  353. * __tostring metamethod for lui controls
  354. */
  355. static int lui_control__tostring(lua_State *L)
  356. {
  357. void *obj = lua_touserdata(L, 1);
  358. const char *type;
  359. if (luaL_getmetafield (L, 1, "__name") != LUA_TNIL) {
  360. type = lua_tostring(L, -1);
  361. lua_pop(L, 1);
  362. } else {
  363. type = "lui_control";
  364. }
  365. lua_pushfstring(L, "%s: %p", type, ((lui_object*)obj)->object);
  366. return 1;
  367. }
  368. /*** Property
  369. * Object: control
  370. * Name: toplevel
  371. * true if the conrol is a toplevel control (i.e. window), false if not.
  372. * This is a read-only property.
  373. *** Property
  374. * Object: control
  375. * Name: visible
  376. * true if the control is / should be visible, false if not. Default is true.
  377. *** Property
  378. * Object: control
  379. * Name: enabled
  380. * true if the control is / should be enabled, false if not. Default is true.
  381. */
  382. static int lui_control__index(lua_State *L)
  383. {
  384. lui_object *lobj = lui_toObject(L, 1);
  385. const char *what = luaL_checkstring(L, 2);
  386. ensure_valid(lobj);
  387. if (strcmp(what, "toplevel") == 0) {
  388. lua_pushboolean(L, uiControlToplevel(lobj->object) != 0);
  389. } else if (strcmp(what, "visible") == 0) {
  390. lua_pushboolean(L, uiControlVisible(lobj->object) != 0);
  391. } else if (strcmp(what, "enabled") == 0) {
  392. lua_pushboolean(L, uiControlEnabled(lobj->object) != 0);
  393. } else if (luaL_getmetafield(L, 1, "__methods") != LUA_TNIL) {
  394. lua_pushvalue(L, 2);
  395. lua_gettable(L, -2);
  396. } else {
  397. lua_pushnil(L);
  398. }
  399. return 1;
  400. }
  401. static int lui_control__newindex(lua_State *L)
  402. {
  403. lui_object *lobj = lui_toObject(L, 1);
  404. const char *what = luaL_checkstring(L, 2);
  405. int val = lua_toboolean(L, 3);
  406. ensure_valid(lobj);
  407. if (strcmp(what, "visible") == 0) {
  408. if (val) {
  409. uiControlShow(lobj->object);
  410. } else {
  411. uiControlHide(lobj->object);
  412. }
  413. } else if (strcmp(what, "enabled") == 0) {
  414. if (val) {
  415. uiControlEnable(lobj->object);
  416. } else {
  417. uiControlDisable(lobj->object);
  418. }
  419. } else {
  420. lua_pushfstring(L, "attempt to set invalid field ('%s')", what);
  421. return lua_error(L);
  422. }
  423. return 1;
  424. }
  425. static void lui_controlSetParent(lua_State *L, int ctl, int parent)
  426. {
  427. if (!lua_isnil(L, ctl)) {
  428. lui_aux_setUservalue(L, ctl, "parent", parent);
  429. }
  430. }
  431. static void lui_controlClearParent(lua_State *L, int ctl)
  432. {
  433. if (!lua_isnil(L, ctl)) {
  434. lua_pushnil(L);
  435. lui_aux_setUservalue(L, ctl, "parent", lua_gettop(L));
  436. lua_pop(L, 1);
  437. }
  438. }
  439. static int lui_controlHasParent(lua_State *L, int ctl)
  440. {
  441. int has = 0;
  442. if (lui_aux_getUservalue(L, ctl, "parent") != LUA_TNIL) {
  443. has = 1;
  444. }
  445. lua_pop(L, 1);
  446. return has;
  447. }
  448. static void lui_controlSetChild(lua_State *L, int ctl, int cld)
  449. {
  450. if (lui_controlHasParent(L, cld)) {
  451. /*return*/ luaL_error(L, "child control already has a parent.");
  452. }
  453. if (lui_aux_getUservalue(L, ctl, "child") != LUA_TNIL) {
  454. lui_controlClearParent(L, lua_gettop(L));
  455. }
  456. lua_pop(L, 1);
  457. lui_aux_setUservalue(L, ctl, "child", cld);
  458. lui_controlSetParent(L, cld, ctl);
  459. }
  460. static void lui_controlInsertChild(lua_State *L, int ctl, int pos, int cld)
  461. {
  462. if (lui_controlHasParent(L, cld)) {
  463. /*return*/ luaL_error(L, "child control already has a parent.");
  464. }
  465. if (lui_aux_getUservalue(L, ctl, "children") == LUA_TNIL) {
  466. lua_pop(L, 1);
  467. lua_newtable(L);
  468. }
  469. int tbl = lua_gettop(L);
  470. lui_aux_tinsert(L, tbl, pos, cld);
  471. lui_aux_setUservalue(L, ctl, "children", tbl);
  472. lui_controlSetParent(L, cld, ctl);
  473. lua_pop(L, 1);
  474. }
  475. static void lui_controlAppendChild(lua_State *L, int ctl, int cld)
  476. {
  477. lui_controlInsertChild(L, ctl, -1, cld);
  478. }
  479. static void lui_controlDelChild(lua_State *L, int ctl, int pos)
  480. {
  481. if (lui_aux_getUservalue(L, ctl, "children") != LUA_TNIL) {
  482. int tbl = lua_gettop(L);
  483. if (lua_rawgeti(L, tbl, pos) != LUA_TNIL) {
  484. lui_controlClearParent(L, lua_gettop(L));
  485. }
  486. lua_pop(L, 1);
  487. lui_aux_tdelete(L, tbl, pos);
  488. }
  489. lua_pop(L, 1);
  490. }
  491. static int lui_controlNchildren(lua_State *L, int ctl)
  492. {
  493. int len = 0;
  494. if (lui_aux_getUservalue(L, ctl, "children") != LUA_TNIL) {
  495. len = lua_rawlen(L, -1);
  496. } else if (lui_aux_getUservalue(L, ctl, "child") != LUA_TNIL) {
  497. len = 1;
  498. }
  499. lua_pop(L, 1);
  500. return len;
  501. }
  502. /*** Method
  503. * Object: control
  504. * Name: numchildren
  505. * Signature: num = control:numchildren()
  506. * return the number of children a control has.
  507. */
  508. static int lui_controlNumChildren(lua_State *L)
  509. {
  510. (void) lui_checkObject(L, 1);
  511. lua_pushinteger(L, lui_controlNchildren(L, 1));
  512. return 1;
  513. }
  514. /*** Method
  515. * Object: control
  516. * Name: getchild
  517. * Signature: control = control:getchild(num)
  518. * return the num'th child of a control, or nil if there is no such child.
  519. */
  520. static int lui_controlGetChild(lua_State *L)
  521. {
  522. (void) lui_checkObject(L, 1);
  523. int which = luaL_checkinteger(L, 2);
  524. if (lui_aux_getUservalue(L, 1, "child") != LUA_TNIL) {
  525. if (which != 1) {
  526. lua_pop(L, 1);
  527. lua_pushnil(L);
  528. }
  529. return 1;
  530. }
  531. lua_pop(L, 1);
  532. if (lui_aux_getUservalue(L, 1, "children") != LUA_TNIL) {
  533. lua_rawgeti(L, lua_gettop(L), which);
  534. return 1;
  535. }
  536. lua_pop(L, 1);
  537. lua_pushnil(L);
  538. return 1;
  539. }
  540. /*** Method
  541. * Object: control
  542. * Name: getparent
  543. * Signature: control = control:getparent()
  544. * return the parent of a control or nil if it has no parent.
  545. */
  546. static int lui_controlGetParent(lua_State *L)
  547. {
  548. lui_object *lobj = lui_checkObject(L, 1);
  549. uiControl *ctl = uiControlParent(uiControl(lobj->object));
  550. lui_findObject(L, ctl);
  551. return 1;
  552. }
  553. /*** Method
  554. * Object: control
  555. * Name: type
  556. * Signature: control:type()
  557. * return the type name for a control
  558. */
  559. static int lui_objectType(lua_State *L)
  560. {
  561. lui_object *lobj = lui_checkObject(L, 1);
  562. if (lobj->object == 0) {
  563. lua_pushnil(L);
  564. lua_pushstring(L, "control has been destroyed");
  565. return 2;
  566. }
  567. int ok = luaL_getmetafield(L, 1, "__name");
  568. if (ok) {
  569. return 1;
  570. }
  571. lua_pushnil(L);
  572. lua_pushstring(L, "unknown control type");
  573. return 2;
  574. }
  575. static int lui_objectSetHandler(lua_State *L, const char *handler, int pos)
  576. {
  577. if (!lua_isnoneornil(L, pos)) {
  578. luaL_argcheck(L, lui_aux_iscallable(L, pos), pos, "expected callable");
  579. }
  580. lui_aux_setUservalue(L, 1, handler, pos);
  581. return 0;
  582. }
  583. static int lui_objectGetHandler(lua_State *L, const char *handler)
  584. {
  585. lui_aux_getUservalue(L, 1, handler);
  586. return 1;
  587. }
  588. /* beware: no stack hygenie. Do that in the concrete handler! */
  589. static int lui_objectHandlerCallback(lua_State *L, const uiControl *control, const char *handler, int first, int narg, int nres)
  590. {
  591. if (lui_findObject(L, control) == LUA_TNIL) {
  592. return 0;
  593. }
  594. int objidx = lua_gettop(L);
  595. int baseargs = 1;
  596. lui_aux_getUservalue(L, objidx, handler);
  597. if (!lui_aux_iscallable(L, -1)) {
  598. return 0;
  599. }
  600. if (luaL_getmetafield(L, -1, "__call") != LUA_TNIL) {
  601. lua_pushvalue(L, -2);
  602. baseargs += 1;
  603. }
  604. lua_pushvalue(L, objidx);
  605. int i;
  606. for (i = 0; i < narg; ++i) {
  607. lua_pushvalue(L, first + i);
  608. }
  609. lua_call(L, narg + baseargs, nres);
  610. return nres;
  611. }
  612. /* generic metamethods for uiControls */
  613. static const luaL_Reg lui_control_meta[] = {
  614. {"__gc", lui_control__gc},
  615. {"__tostring", lui_control__tostring},
  616. {"__index", lui_control__index},
  617. {"__newindex", lui_control__newindex},
  618. {0, 0}
  619. };
  620. /* generic methods for uiControls */
  621. static const luaL_Reg lui_control_methods[] = {
  622. {"type", lui_objectType},
  623. {"getparent", lui_controlGetParent},
  624. {"getchild", lui_controlGetChild},
  625. {"numchildren", lui_controlNumChildren},
  626. {0, 0}
  627. };
  628. /* utility object metamethods ********************************************/
  629. static int lui_utility__gc(lua_State *L)
  630. {
  631. lui_object *lobj = lui_toObject(L, 1);
  632. if (lobj->object) {
  633. DEBUGMSG("lui_utility__gc (%s)", lui_debug_controlTostring(L, 1));
  634. free((void*)(lobj->object));
  635. lobj->object = 0;
  636. }
  637. return 0;
  638. }
  639. static int lui_utility__index(lua_State *L)
  640. {
  641. (void) lui_checkObject(L, 1);
  642. if (luaL_getmetafield(L, 1, "__methods") != LUA_TNIL) {
  643. lua_pushvalue(L, 2);
  644. lua_gettable(L, -2);
  645. } else {
  646. lua_pushnil(L);
  647. }
  648. return 1;
  649. }
  650. static int lui_utility__newindex(lua_State *L)
  651. {
  652. const char *what = luaL_checkstring(L, 2);
  653. lua_pushfstring(L, "attempt to set invalid field ('%s')", what);
  654. return lua_error(L);
  655. }
  656. static const luaL_Reg lui_utility_meta[] = {
  657. {"__gc", lui_utility__gc},
  658. {"__tostring", lui_control__tostring},
  659. {"__index", lui_utility__index},
  660. {"__newindex", lui_utility__newindex},
  661. {0, 0}
  662. };
  663. /* helpers to register types for lui **************************************/
  664. static void lui_add_control_type(lua_State *L, const char *name, const struct luaL_Reg *methods, const struct luaL_Reg *meta)
  665. {
  666. luaL_newmetatable(L, name);
  667. luaL_setfuncs(L, lui_control_meta, 0);
  668. if (meta) {
  669. luaL_setfuncs(L, meta, 0);
  670. }
  671. /* add methods */
  672. lua_pushstring(L, "__methods");
  673. luaL_newlib(L, lui_control_methods);
  674. if (methods) {
  675. luaL_setfuncs(L, methods, 0);
  676. }
  677. lua_settable(L, -3);
  678. /* clean up stack */
  679. lua_pop(L, 1);
  680. }
  681. static void lui_add_utility_type(lua_State *L, const char *name, const struct luaL_Reg *methods, const struct luaL_Reg *meta)
  682. {
  683. luaL_newmetatable(L, name);
  684. luaL_setfuncs(L, lui_utility_meta, 0);
  685. if (meta) {
  686. luaL_setfuncs(L, meta, 0);
  687. }
  688. if (methods) {
  689. lua_pushstring(L, "__methods");
  690. lua_newtable(L);
  691. luaL_setfuncs(L, methods, 0);
  692. lua_settable(L, -3);
  693. }
  694. /* clean up stack */
  695. lua_pop(L, 1);
  696. }
  697. /* color handling helper functions ****************************************/
  698. static int lui_aux_pushRgbaAsTable(lua_State *L, double r, double g, double b, double a)
  699. {
  700. lua_newtable(L);
  701. lua_pushnumber(L, r);
  702. lua_setfield(L, -2, "r");
  703. lua_pushnumber(L, g);
  704. lua_setfield(L, -2, "g");
  705. lua_pushnumber(L, b);
  706. lua_setfield(L, -2, "b");
  707. lua_pushnumber(L, a);
  708. lua_setfield(L, -2, "a");
  709. return 1;
  710. }
  711. static int lui_aux_rgbaFromTable(lua_State *L, int pos, double *r, double *g, double *b, double *a)
  712. {
  713. if (pos < 0) {
  714. pos = lua_gettop(L) + 1 + pos;
  715. }
  716. lua_getfield(L, pos, "r");
  717. *r = luaL_checknumber(L, -1);
  718. lua_getfield(L, pos, "g");
  719. *g = luaL_checknumber(L, -1);
  720. lua_getfield(L, pos, "b");
  721. *b = luaL_checknumber(L, -1);
  722. lua_getfield(L, pos, "a");
  723. *a = luaL_optnumber(L, -1, 1);
  724. lua_pop(L, 4);
  725. return 1;
  726. }
  727. /* include controls *******************************************************/
  728. #include "container.inc.c"
  729. #include "controls.inc.c"
  730. #include "menu.inc.c"
  731. #include "text.inc.c"
  732. #include "draw.inc.c"
  733. #include "area.inc.c"
  734. #include "dialog.inc.c"
  735. #include "image.inc.c"
  736. #include "table.inc.c"
  737. /* misc functions *********************************************************/
  738. /*** Function
  739. * Name: init
  740. * Signature: lui.init()
  741. * initialize lui, must be called before anything else.
  742. */
  743. static int lui_init(lua_State *L)
  744. {
  745. uiInitOptions o;
  746. memset(&o, 0, sizeof(o));
  747. const char *emsg = uiInit(&o);
  748. if (emsg) {
  749. lua_pushnil(L);
  750. lua_pushstring(L, emsg);
  751. uiFreeInitError(emsg);
  752. return 2;
  753. }
  754. lui_initialized = 1;
  755. lua_pushboolean(L, 1);
  756. return 1;
  757. }
  758. /*** Function
  759. * Name: finalize
  760. * Signature: lui.finalize()
  761. * finalize lui and clean up any used resources. May be omitted as lui is
  762. * finalized when the program terminates.
  763. */
  764. static int lui_finalize(lua_State *L)
  765. {
  766. if (lui_initialized) {
  767. DEBUGMSG("finalizing...");
  768. lui_initialized = 0;
  769. /* clean out control registry. The actual loop running through the
  770. * registry must be repeated until there are no more valid objects
  771. * left, as any of the objects in the registry may be a container
  772. * holding other objects, that will have to be released before they
  773. * can be collected.
  774. */
  775. lua_pushstring(L, LUI_OBJECT_REGISTRY);
  776. lua_gettable(L, LUA_REGISTRYINDEX);
  777. int tbl = lua_gettop(L);
  778. lua_pushnil(L);
  779. int obidx = lua_gettop(L) + 1;
  780. while (lua_next(L, tbl) != 0) {
  781. lui_object *lobj = lui_isObject(L, -1);
  782. if (lobj && lobj->object) {
  783. DEBUGMSG("finalizing %s", lui_debug_controlTostring(L, -1));
  784. luaL_callmeta(L, obidx, "__gc");
  785. }
  786. lua_settop(L, obidx - 1);
  787. }
  788. lua_pop(L, 1);
  789. lua_pushstring(L, LUI_OBJECT_REGISTRY);
  790. lua_pushnil(L);
  791. lua_settable(L, LUA_REGISTRYINDEX);
  792. uiUninit();
  793. }
  794. return 0;
  795. }
  796. /*** Function
  797. * Name: main
  798. * Signature: lui.main()
  799. * enter the main event processing loop.
  800. */
  801. static int lui_main(lua_State *L)
  802. {
  803. uiMain();
  804. return 0;
  805. }
  806. /*** Function
  807. * Name: mainsteps
  808. * Signature: lui.mainsteps()
  809. * initialize your own main loop with lui.mainstep() instead of using
  810. * lui.main(). So, if you want to use your own main loop instead of
  811. * lui.main(), you do it like this:
  812. *
  813. * lui.mainsteps()
  814. * while lui.mainstep() do
  815. * -- stuff
  816. * end
  817. */
  818. static int lui_mainSteps(lua_State *L)
  819. {
  820. uiMainSteps();
  821. return 0;
  822. }
  823. /*** Function
  824. * Name: mainstep
  825. * Signature: running = lui.mainstep(wait = false)
  826. * perform one round of the main event processing loop. Returns true if the
  827. * program should continue to run, or false if it should quit (i.e. lui.quit()
  828. * was called).
  829. */
  830. static int lui_mainStep(lua_State *L)
  831. {
  832. int wait = lua_toboolean(L, 1);
  833. int cont = uiMainStep(wait);
  834. lua_pushboolean(L, cont);
  835. return 1;
  836. }
  837. /*** Function
  838. * Name: onnextidle
  839. * Signature: lui.onnextidle(function)
  840. * register a function to be called the next time when there are no events
  841. * pending. This function will only be called once.
  842. */
  843. static void lui_onNextIdleCallback(void *data)
  844. {
  845. DEBUGMSG("lui_onNextIdleCallback");
  846. lua_State *L = (lua_State*) data;
  847. lua_pushstring(L, LUI_OBJECT_REGISTRY);
  848. lua_gettable(L, LUA_REGISTRYINDEX);
  849. lua_pushstring(L, "lui_onnextidle");
  850. if (lua_gettable(L, -2) != LUA_TNIL) {
  851. lua_call(L, 0, 0);
  852. }
  853. lua_pop(L, 2);
  854. }
  855. static int lui_onNextIdle(lua_State *L)
  856. {
  857. if (!lua_isnoneornil(L, 1)) {
  858. luaL_checktype(L, 1, LUA_TFUNCTION);
  859. }
  860. lua_pushstring(L, LUI_OBJECT_REGISTRY);
  861. lua_gettable(L, LUA_REGISTRYINDEX);
  862. lua_pushstring(L, "lui_onnextidle");
  863. lua_pushvalue(L, 1);
  864. lua_settable(L, -3);
  865. lua_pop(L, 1);
  866. uiQueueMain(lui_onNextIdleCallback, L);
  867. return 0;
  868. }
  869. /*** Function
  870. * Name: quit
  871. * Signature: lui.quit()
  872. * terminate the main event processing loop. After calling this, lui.main()
  873. * returns and lui.mainstep() returns false.
  874. */
  875. static int lui_quit(lua_State *L)
  876. {
  877. uiQuit();
  878. return 0;
  879. }
  880. /*** Function
  881. * Name: onshouldquit
  882. * Signature: lui.onshouldquit(function)
  883. * register a function to be called when the quit menu item (see above) is clicked.
  884. */
  885. static int lui_onShouldQuit(lua_State *L)
  886. {
  887. if (!lua_isnoneornil(L, 1)) {
  888. luaL_checktype(L, 1, LUA_TFUNCTION);
  889. }
  890. lua_pushstring(L, LUI_OBJECT_REGISTRY);
  891. lua_gettable(L, LUA_REGISTRYINDEX);
  892. lua_pushstring(L, "lui_onshouldquit");
  893. lua_pushvalue(L, 1);
  894. lua_settable(L, -3);
  895. lua_pop(L, 1);
  896. return 0;
  897. }
  898. static int lui_onShouldQuitCallback(void *data)
  899. {
  900. DEBUGMSG("lui_onShouldQuitCallback");
  901. int ok = 1;
  902. lua_State *L = (lua_State*) data;
  903. lua_pushstring(L, LUI_OBJECT_REGISTRY);
  904. lua_gettable(L, LUA_REGISTRYINDEX);
  905. lua_pushstring(L, "lui_onshouldquit");
  906. if (lua_gettable(L, -2) != LUA_TNIL) {
  907. lua_call(L, 0, 1);
  908. ok = lua_toboolean(L, -1);
  909. lua_pop(L, 1);
  910. }
  911. lua_pop(L, 2);
  912. return ok;
  913. }
  914. static int lui_fileDialog(lua_State *L, uiWindow *parent, char* (dlgfn)(uiWindow*))
  915. {
  916. char *file = dlgfn(parent);
  917. if (file) {
  918. lua_pushstring(L, file);
  919. } else {
  920. lua_pushnil(L);
  921. }
  922. return 1;
  923. }
  924. /*** Function
  925. * Name: openfile
  926. * Signature: filename = lui.openfile(window)
  927. * open a system file open dialog. Returns the path and name of the file to
  928. * open, or nil if the dialog was cancelled.
  929. */
  930. static int lui_openFile(lua_State *L)
  931. {
  932. lui_object *lobj = lui_checkWindow(L, 1);
  933. return lui_fileDialog(L, uiWindow(lobj->object), uiOpenFile);
  934. }
  935. /*** Function
  936. * Name: savefile
  937. * Signature: filename = lui.savefile(window)
  938. * open a system file save dialog. Returns the path and name of the file to
  939. * save to, or nil if the dialog was cancelled.
  940. */
  941. static int lui_saveFile(lua_State *L)
  942. {
  943. lui_object *lobj = lui_checkWindow(L, 1);
  944. return lui_fileDialog(L, uiWindow(lobj->object), uiSaveFile);
  945. }
  946. /*** Function
  947. * Name: msgbox
  948. * Signature: lui.msgbox(window, title, text)
  949. * open a system message box.
  950. */
  951. static int lui_msgBox(lua_State *L)
  952. {
  953. lui_object *lobj = lui_checkWindow(L, 1);
  954. const char *title = luaL_checkstring(L, 2);
  955. const char *text = luaL_checkstring(L, 3);
  956. uiMsgBox(uiWindow(lobj->object), title, text);
  957. return 0;
  958. }
  959. /*** Function
  960. * Name: errorbox
  961. * Signature: lui.errorbox(window, title, text)
  962. * open a system error message box.
  963. */
  964. static int lui_errorBox(lua_State *L)
  965. {
  966. lui_object *lobj = lui_checkWindow(L, 1);
  967. const char *title = luaL_checkstring(L, 2);
  968. const char *text = luaL_checkstring(L, 3);
  969. uiMsgBoxError(uiWindow(lobj->object), title, text);
  970. return 0;
  971. }
  972. /* module function list table
  973. */
  974. static const struct luaL_Reg lui_funcs [] ={
  975. {"init", lui_init},
  976. {"finalize", lui_finalize},
  977. {"main", lui_main},
  978. {"mainsteps", lui_mainSteps},
  979. {"mainstep", lui_mainStep},
  980. {"onnextidle", lui_onNextIdle},
  981. {"quit", lui_quit},
  982. {"onshouldquit", lui_onShouldQuit},
  983. {"openfile", lui_openFile},
  984. {"savefile", lui_saveFile},
  985. {"msgbox", lui_msgBox},
  986. {"errorbox", lui_errorBox},
  987. {0, 0}
  988. };
  989. /* luaopen_lui
  990. *
  991. * open and initialize this library
  992. */
  993. int luaopen_lui(lua_State *L)
  994. {
  995. /* exported function table. Create a metatable with a __gc field in order
  996. * to ensure that on lua closedown lui_finalize() gets called.
  997. */
  998. luaL_newlib(L, lui_funcs);
  999. lua_newtable(L);
  1000. lua_pushstring(L, "__gc");
  1001. lua_pushcfunction(L, lui_finalize);
  1002. lua_settable(L, -3);
  1003. lua_setmetatable(L, -2);
  1004. lui_init_container(L);
  1005. lui_init_controls(L);
  1006. lui_init_menu(L);
  1007. lui_init_text(L);
  1008. lui_init_draw(L);
  1009. lui_init_area(L);
  1010. lui_init_dialog(L);
  1011. lui_init_image(L);
  1012. lui_init_table(L);
  1013. /* create control registry */
  1014. lua_newtable(L);
  1015. lua_newtable(L);
  1016. lua_pushstring(L, "v");
  1017. lua_setfield(L, -2, "__mode");
  1018. lua_setmetatable(L, -2);
  1019. lua_setfield(L, LUA_REGISTRYINDEX, LUI_OBJECT_REGISTRY);
  1020. /* register global onShouldQuit handler */
  1021. uiOnShouldQuit(lui_onShouldQuitCallback, L);
  1022. return 1;
  1023. }