mirror of https://github.com/bjornbytes/lovr.git
288 lines
7.0 KiB
C
288 lines
7.0 KiB
C
#include "luax.h"
|
|
#include "platform.h"
|
|
#include "util.h"
|
|
#include "lib/sds/sds.h"
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <stdbool.h>
|
|
|
|
static int luax_meta__tostring(lua_State* L) {
|
|
lua_getfield(L, -1, "name");
|
|
return 1;
|
|
}
|
|
|
|
static int luax_meta__gc(lua_State* L) {
|
|
Proxy* p = lua_touserdata(L, 1);
|
|
lovrGenericRelease(p->ref);
|
|
return 0;
|
|
}
|
|
|
|
static int luax_module__gc(lua_State* L) {
|
|
lua_getfield(L, LUA_REGISTRYINDEX, "_lovrmodules");
|
|
for (int i = luax_len(L, 2); i >= 1; i--) {
|
|
lua_rawgeti(L, 2, i);
|
|
luax_destructor destructor = (luax_destructor) lua_tocfunction(L, -1);
|
|
destructor();
|
|
lua_pop(L, 1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// A version of print that uses lovrLog, for platforms that need it (Android)
|
|
int luax_print(lua_State* L) {
|
|
sds str = sdsempty();
|
|
int n = lua_gettop(L); /* number of arguments */
|
|
int i;
|
|
|
|
lua_getglobal(L, "tostring");
|
|
for (i=1; i<=n; i++) {
|
|
const char *s;
|
|
lua_pushvalue(L, -1); /* function to be called */
|
|
lua_pushvalue(L, i); /* value to print */
|
|
lua_call(L, 1, 1);
|
|
s = lua_tostring(L, -1); /* get result */
|
|
if (s == NULL)
|
|
return luaL_error(L, LUA_QL("tostring") " must return a string to "
|
|
LUA_QL("print"));
|
|
if (i>1) str = sdscat(str, "\t");
|
|
str = sdscat(str, s);
|
|
lua_pop(L, 1); /* pop result */
|
|
}
|
|
lovrLog("%s", str);
|
|
|
|
sdsfree(str);
|
|
return 0;
|
|
}
|
|
|
|
void luax_setmainthread(lua_State *L) {
|
|
#if LUA_VERSION_NUM < 502
|
|
lua_pushthread(L);
|
|
lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD);
|
|
#endif
|
|
}
|
|
|
|
void luax_atexit(lua_State* L, luax_destructor destructor) {
|
|
lua_getfield(L, LUA_REGISTRYINDEX, "_lovrmodules");
|
|
|
|
if (lua_isnil(L, -1)) {
|
|
lua_newtable(L);
|
|
lua_replace(L, -2);
|
|
|
|
// Userdata sentinel since tables don't have __gc (yet)
|
|
lua_newuserdata(L, sizeof(void*));
|
|
lua_createtable(L, 0, 1);
|
|
lua_pushcfunction(L, luax_module__gc);
|
|
lua_setfield(L, -2, "__gc");
|
|
lua_setmetatable(L, -2);
|
|
lua_setfield(L, -2, "");
|
|
|
|
// Write to the registry
|
|
lua_pushvalue(L, -1);
|
|
lua_setfield(L, LUA_REGISTRYINDEX, "_lovrmodules");
|
|
}
|
|
|
|
int length = luax_len(L, -1);
|
|
lua_pushcfunction(L, (lua_CFunction) destructor);
|
|
lua_rawseti(L, -2, length + 1);
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
void luax_registerloader(lua_State* L, lua_CFunction loader, int index) {
|
|
lua_getglobal(L, "table");
|
|
lua_getfield(L, -1, "insert");
|
|
lua_getglobal(L, "package");
|
|
lua_getfield(L, -1, "loaders");
|
|
lua_remove(L, -2);
|
|
if (lua_istable(L, -1)) {
|
|
lua_pushinteger(L, index);
|
|
lua_pushcfunction(L, loader);
|
|
lua_call(L, 3, 0);
|
|
}
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
void _luax_registertype(lua_State* L, const char* name, const luaL_Reg* functions) {
|
|
|
|
// Push metatable
|
|
luaL_newmetatable(L, name);
|
|
lua_getmetatable(L, -1);
|
|
|
|
// m.__index = m
|
|
lua_pushvalue(L, -1);
|
|
lua_setfield(L, -1, "__index");
|
|
|
|
// m.__gc = gc
|
|
lua_pushcfunction(L, luax_meta__gc);
|
|
lua_setfield(L, -2, "__gc");
|
|
|
|
// m.name = name
|
|
lua_pushstring(L, name);
|
|
lua_setfield(L, -2, "name");
|
|
|
|
// m.__tostring
|
|
lua_pushcfunction(L, luax_meta__tostring);
|
|
lua_setfield(L, -2, "__tostring");
|
|
|
|
// Register class functions
|
|
if (functions) {
|
|
luaL_register(L, NULL, functions);
|
|
}
|
|
|
|
// Pop metatable
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
void _luax_extendtype(lua_State* L, const char* name, const luaL_Reg* baseFunctions, const luaL_Reg* functions) {
|
|
_luax_registertype(L, name, functions);
|
|
luaL_getmetatable(L, name);
|
|
luaL_register(L, NULL, baseFunctions);
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
void* _luax_totype(lua_State* L, int index, Type type) {
|
|
Proxy* p = lua_touserdata(L, index);
|
|
|
|
if (p && (p->type == type || lovrTypeInfo[p->type].super == type)) {
|
|
return p->ref;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void* _luax_checktype(lua_State* L, int index, Type type, const char* debug) {
|
|
void* object = _luax_totype(L, index, type);
|
|
|
|
if (!object) {
|
|
luaL_typerror(L, index, debug);
|
|
}
|
|
|
|
return object;
|
|
}
|
|
|
|
// Registers the userdata on the top of the stack in the registry.
|
|
void luax_pushobject(lua_State* L, void* object) {
|
|
if (!object) {
|
|
lua_pushnil(L);
|
|
return;
|
|
}
|
|
|
|
lua_getfield(L, LUA_REGISTRYINDEX, "_lovrobjects");
|
|
|
|
// Create the registry if it doesn't exist yet
|
|
if (lua_isnil(L, -1)) {
|
|
lua_newtable(L);
|
|
lua_replace(L, -2);
|
|
|
|
// Create the metatable
|
|
lua_newtable(L);
|
|
|
|
// __mode = v
|
|
lua_pushliteral(L, "v");
|
|
lua_setfield(L, -2, "__mode");
|
|
|
|
// Set the metatable
|
|
lua_setmetatable(L, -2);
|
|
|
|
// Write the table to the registry
|
|
lua_pushvalue(L, -1);
|
|
lua_setfield(L, LUA_REGISTRYINDEX, "_lovrobjects");
|
|
}
|
|
|
|
lua_pushlightuserdata(L, object);
|
|
lua_gettable(L, -2);
|
|
|
|
if (lua_isnil(L, -1)) {
|
|
lua_pop(L, 1);
|
|
} else {
|
|
lua_remove(L, -2);
|
|
return;
|
|
}
|
|
|
|
// Allocate userdata
|
|
Ref* ref = (Ref*) object;
|
|
Proxy* p = (Proxy*) lua_newuserdata(L, sizeof(Proxy));
|
|
luaL_getmetatable(L, lovrTypeInfo[ref->type].name);
|
|
lua_setmetatable(L, -2);
|
|
lovrRetain(object);
|
|
p->type = ref->type;
|
|
p->ref = ref;
|
|
|
|
// Write to registry and remove registry, leaving userdata on stack
|
|
lua_pushlightuserdata(L, object);
|
|
lua_pushvalue(L, -2);
|
|
lua_settable(L, -4);
|
|
lua_remove(L, -2);
|
|
}
|
|
|
|
void luax_vthrow(lua_State* L, const char* format, va_list args) {
|
|
lua_pushvfstring(L, format, args);
|
|
lua_error(L);
|
|
}
|
|
|
|
// An implementation of luaL_traceback for Lua 5.1
|
|
void luax_traceback(lua_State* L, lua_State* T, const char* message, int level) {
|
|
if (!lua_checkstack(L, 5)) {
|
|
return;
|
|
}
|
|
lua_getglobal(L, "debug");
|
|
if (!lua_istable(L, -1)) {
|
|
lua_pop(L, 1);
|
|
return;
|
|
}
|
|
lua_getfield(L, -1, "traceback");
|
|
if (!lua_isfunction(L, -1)) {
|
|
lua_pop(L, 2);
|
|
return;
|
|
}
|
|
lua_remove(L, -2); // Pop debug object
|
|
lua_pushthread(T);
|
|
lua_pushstring(L, message);
|
|
lua_pushinteger(L, level);
|
|
lua_call(L, 3, 1);
|
|
}
|
|
|
|
int luax_getstack(lua_State *L) {
|
|
luax_traceback(L, L, lua_tostring(L, 1), 2);
|
|
return 1;
|
|
}
|
|
|
|
void luax_pushconf(lua_State* L) {
|
|
lua_getfield(L, LUA_REGISTRYINDEX, "_lovrconf");
|
|
}
|
|
|
|
int luax_setconf(lua_State* L) {
|
|
luax_pushconf(L);
|
|
lovrAssert(lua_isnil(L, -1), "Unable to set lovr.conf multiple times");
|
|
lua_pop(L, 1);
|
|
lua_setfield(L, LUA_REGISTRYINDEX, "_lovrconf");
|
|
return 0;
|
|
}
|
|
|
|
void luax_readcolor(lua_State* L, int index, Color* color) {
|
|
color->r = color->g = color->b = color->a = 1.f;
|
|
|
|
if (lua_istable(L, 1)) {
|
|
for (int i = 1; i <= 4; i++) {
|
|
lua_rawgeti(L, 1, i);
|
|
}
|
|
color->r = luax_checkfloat(L, -4);
|
|
color->g = luax_checkfloat(L, -3);
|
|
color->b = luax_checkfloat(L, -2);
|
|
color->a = luax_optfloat(L, -1, 1.);
|
|
lua_pop(L, 4);
|
|
} else if (lua_gettop(L) >= index + 2) {
|
|
color->r = luax_checkfloat(L, index);
|
|
color->g = luax_checkfloat(L, index + 1);
|
|
color->b = luax_checkfloat(L, index + 2);
|
|
color->a = luax_optfloat(L, index + 3, 1.);
|
|
} else if (lua_gettop(L) == index) {
|
|
uint32_t x = luaL_checkinteger(L, index);
|
|
color->r = ((x >> 16) & 0xff) / 255.f;
|
|
color->g = ((x >> 8) & 0xff) / 255.f;
|
|
color->b = ((x >> 0) & 0xff) / 255.f;
|
|
color->a = 1.f;
|
|
} else {
|
|
luaL_error(L, "Invalid color, expected a hexcode, 3 numbers, 4 numbers, or a table");
|
|
}
|
|
}
|