diff --git a/CMakeLists.txt b/CMakeLists.txt index 45b7b5dc..8df90fb8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -345,8 +345,8 @@ if(LOVR_ENABLE_AUDIO) src/modules/audio/source.c src/modules/audio/microphone.c src/api/l_audio.c - src/api/l_source.c - src/api/l_microphone.c + src/api/l_audio_source.c + src/api/l_audio_microphone.c ) endif() @@ -362,12 +362,12 @@ if(LOVR_ENABLE_DATA) src/modules/data/soundData.c src/modules/data/textureData.c src/api/l_data.c - src/api/l_audioStream.c - src/api/l_blob.c - src/api/l_modelData.c - src/api/l_rasterizer.c - src/api/l_soundData.c - src/api/l_textureData.c + src/api/l_data_audioStream.c + src/api/l_data_blob.c + src/api/l_data_modelData.c + src/api/l_data_rasterizer.c + src/api/l_data_soundData.c + src/api/l_data_textureData.c src/lib/stb/stb_image.c src/lib/stb/stb_image_write.c src/lib/stb/stb_truetype.c @@ -407,14 +407,14 @@ if(LOVR_ENABLE_GRAPHICS) src/modules/graphics/shader.c src/modules/graphics/texture.c src/api/l_graphics.c - src/api/l_canvas.c - src/api/l_font.c - src/api/l_material.c - src/api/l_mesh.c - src/api/l_model.c - src/api/l_shader.c - src/api/l_shaderBlock.c - src/api/l_texture.c + src/api/l_graphics_canvas.c + src/api/l_graphics_font.c + src/api/l_graphics_material.c + src/api/l_graphics_mesh.c + src/api/l_graphics_model.c + src/api/l_graphics_shader.c + src/api/l_graphics_shaderBlock.c + src/api/l_graphics_texture.c src/resources/shaders.c src/lib/glad/glad.c ) @@ -468,9 +468,9 @@ if(LOVR_ENABLE_MATH) src/modules/math/pool.c src/modules/math/randomGenerator.c src/api/l_math.c - src/api/l_curve.c - src/api/l_randomGenerator.c - src/api/l_vectors.c + src/api/l_math_curve.c + src/api/l_math_randomGenerator.c + src/api/l_math_vectors.c src/lib/noise1234/noise1234.c ) endif() @@ -480,10 +480,10 @@ if(LOVR_ENABLE_PHYSICS) target_sources(lovr PRIVATE src/modules/physics/physics.c src/api/l_physics.c - src/api/l_collider.c - src/api/l_joints.c - src/api/l_shapes.c - src/api/l_world.c + src/api/l_physics_collider.c + src/api/l_physics_joints.c + src/api/l_physics_shapes.c + src/api/l_physics_world.c ) endif() @@ -492,9 +492,9 @@ if(LOVR_ENABLE_THREAD) target_sources(lovr PRIVATE src/modules/thread/channel.c src/modules/thread/thread.c - src/api/l_thread_module.c - src/api/l_channel.c src/api/l_thread.c + src/api/l_thread_channel.c + src/api/l_thread_thread.c src/lib/tinycthread/tinycthread.c ) endif() diff --git a/Tupfile b/Tupfile index 772a8ed9..afd800be 100644 --- a/Tupfile +++ b/Tupfile @@ -1,5 +1,6 @@ include_rules +# core SRC += src/main.c SRC += src/core/arr.c SRC += src/core/fs.c @@ -10,94 +11,62 @@ SRC += src/core/ref.c SRC += src/core/utf.c SRC += src/core/util.c SRC += src/core/zip.c + +# modules +SRC_@(AUDIO) += src/modules/audio/*.c +SRC_@(DATA) += src/modules/data/*.c +SRC_@(EVENT) += src/modules/event/*.c +SRC_@(FILESYSTEM) += src/modules/filesystem/*.c +SRC_@(GRAPHICS) += src/modules/graphics/*.c +SRC_@(HEADSET) += src/modules/headset/headset.c +SRC_@(HEADSET)@(SIMULATOR) += src/modules/headset/desktop.c +SRC_@(HEADSET)@(OPENVR) += src/modules/headset/openvr.c +SRC_@(HEADSET)@(OPENXR) += src/modules/headset/openxr.c +SRC_@(HEADSET)@(OCULUS) += src/modules/headset/oculus.c +SRC_@(HEADSET)@(VRAPI) += src/modules/headset/oculus_mobile.c +SRC_@(HEADSET)@(WEBVR) += src/modules/headset/webvr.c +SRC_@(HEADSET)@(LEAP) += src/modules/headset/leap.c +SRC_@(MATH) += src/modules/math/*.c +SRC_@(PHYSICS) += src/modules/physics/*.c +SRC_@(THREAD) += src/modules/thread/*.c +SRC_@(TIMER) += src/modules/timer/*.c + +# lib +SRC += src/lib/stb/*.c +SRC_@(DATA) += src/lib/jsmn/jsmn.c +SRC_@(GRAPHICS) += src/lib/glad/glad.c +SRC_@(MATH) += src/lib/noise1234/noise1234.c +SRC_@(THREAD) += src/lib/tinycthread/tinycthread.c + +# api SRC += src/api/api.c SRC += src/api/l_lovr.c -SRC += src/lib/stb/*.c - -SRC_@(AUDIO) += src/modules/audio/*.c -SRC_@(AUDIO) += src/api/l_audio.c -SRC_@(AUDIO) += src/api/l_microphone.c -SRC_@(AUDIO) += src/api/l_source.c - -SRC_@(DATA) += src/modules/data/*.c -SRC_@(DATA) += src/lib/jsmn/jsmn.c -SRC_@(DATA) += src/api/l_data.c -SRC_@(DATA) += src/api/l_audioStream.c -SRC_@(DATA) += src/api/l_blob.c -SRC_@(DATA) += src/api/l_modelData.c -SRC_@(DATA) += src/api/l_rasterizer.c -SRC_@(DATA) += src/api/l_soundData.c -SRC_@(DATA) += src/api/l_textureData.c - -SRC_@(EVENT) += src/modules/event/*.c -SRC_@(EVENT) += src/api/l_event.c - -SRC_@(FILESYSTEM) += src/modules/filesystem/*.c -SRC_@(FILESYSTEM) += src/api/l_filesystem.c - -SRC_@(GRAPHICS) += src/modules/graphics/*.c -SRC_@(GRAPHICS) += src/resources/shaders.c -SRC_@(GRAPHICS) += src/lib/glad/glad.c -SRC_@(GRAPHICS) += src/api/l_graphics.c -SRC_@(GRAPHICS) += src/api/l_canvas.c -SRC_@(GRAPHICS) += src/api/l_font.c -SRC_@(GRAPHICS) += src/api/l_material.c -SRC_@(GRAPHICS) += src/api/l_mesh.c -SRC_@(GRAPHICS) += src/api/l_model.c -SRC_@(GRAPHICS) += src/api/l_shader.c -SRC_@(GRAPHICS) += src/api/l_shaderBlock.c -SRC_@(GRAPHICS) += src/api/l_texture.c - -SRC_@(HEADSET) += src/modules/headset/headset.c -SRC_@(HEADSET) += src/api/l_headset.c -ifeq (@(HEADSET),y) - SRC_@(SIMULATOR) += src/modules/headset/desktop.c - SRC_@(OPENVR) += src/modules/headset/openvr.c - SRC_@(OPENXR) += src/modules/headset/openxr.c - SRC_@(OCULUS) += src/modules/headset/oculus.c - SRC_@(VRAPI) += src/modules/headset/oculus_mobile.c - SRC_@(WEBVR) += src/modules/headset/webvr.c - SRC_@(LEAP) += src/modules/headset/leap.c -endif - -SRC_@(MATH) += src/modules/math/*.c -SRC_@(MATH) += src/lib/noise1234/noise1234.c -SRC_@(MATH) += src/api/l_math.c -SRC_@(MATH) += src/api/l_curve.c -SRC_@(MATH) += src/api/l_randomGenerator.c -SRC_@(MATH) += src/api/l_vectors.c - -SRC_@(PHYSICS) += src/modules/physics/*.c -SRC_@(PHYSICS) += src/api/l_physics.c -SRC_@(PHYSICS) += src/api/l_collider.c -SRC_@(PHYSICS) += src/api/l_joints.c -SRC_@(PHYSICS) += src/api/l_shapes.c -SRC_@(PHYSICS) += src/api/l_world.c - -SRC_@(THREAD) += src/modules/thread/*.c -SRC_@(THREAD) += src/lib/tinycthread/tinycthread.c -SRC_@(THREAD) += src/api/l_thread_module.c -SRC_@(THREAD) += src/api/l_thread.c -SRC_@(THREAD) += src/api/l_channel.c - -SRC_@(TIMER) += src/modules/timer/*.c -SRC_@(TIMER) += src/api/l_timer.c - +SRC_@(AUDIO) += src/api/l_audio*.c +SRC_@(DATA) += src/api/l_data*.c +SRC_@(EVENT) += src/api/l_event*.c +SRC_@(FILESYSTEM) += src/api/l_filesystem*.c +SRC_@(GRAPHICS) += src/api/l_graphics*.c +SRC_@(HEADSET) += src/api/l_headset*.c +SRC_@(MATH) += src/api/l_math*.c +SRC_@(PHYSICS) += src/api/l_physics*.c +SRC_@(THREAD) += src/api/l_thread*.c +SRC_@(TIMER) += src/api/l_timer*.c SRC_@(JSON) += src/lib/lua-cjson/*.c SRC_@(ENET) += src/lib/lua-enet/*.c +# resources RES += src/resources/boot.lua RES += src/resources/VarelaRound.ttf RES_@(OPENVR) += src/resources/*.json +SRC_@(GRAPHICS) += src/resources/shaders.c -# Convert resources to binary headers using xxd -: foreach $(RES) $(RES_y) |> !xxd |> %f.h +## build: +# 1 [XD] resources -> bin headers +# 2 [CC] compile .c -> .o +# 3 [LD] link .o -> exe +# 4 [CP] copy external libs -> libs folder -# Compile C source files to object files -: foreach $(SRC) $(SRC_y) | src/resources/*.h |> !cc |> .obj/%B.o - -# Link object files into executable +: foreach $(RES) $(RES_y) |> !xd |> %f.h +: foreach $(SRC) $(SRC_y) $(SRC_yy) | src/resources/*.h |> !cc |> .obj/%B.o : .obj/*.o |> !ld |> lovr - -# Copy external shared libraries to libs folder : foreach $(LIBS) |> !cp |> libs/%b diff --git a/Tuprules.tup b/Tuprules.tup index c388bc80..06562087 100644 --- a/Tuprules.tup +++ b/Tuprules.tup @@ -113,5 +113,5 @@ LDFLAGS += @(EXTRA_LDFLAGS) ## Macros !cc = |> ^ CC %b^ @(CC) $(CFLAGS_y) $(CFLAGS) -c %f -o %o |> !ld = |> ^ LD %o^ @(CC) -o %o %f $(LDFLAGS_y) $(LDFLAGS) |> -!xxd = |> ^ XD %f^ xxd -i %f > %o |> +!xd = |> ^ XD %f^ xxd -i %f > %o |> !cp = |> ^ CP %b^ cp %f %o |> diff --git a/src/api/l_microphone.c b/src/api/l_audio_microphone.c similarity index 100% rename from src/api/l_microphone.c rename to src/api/l_audio_microphone.c diff --git a/src/api/l_source.c b/src/api/l_audio_source.c similarity index 100% rename from src/api/l_source.c rename to src/api/l_audio_source.c diff --git a/src/api/l_audioStream.c b/src/api/l_data_audioStream.c similarity index 100% rename from src/api/l_audioStream.c rename to src/api/l_data_audioStream.c diff --git a/src/api/l_blob.c b/src/api/l_data_blob.c similarity index 100% rename from src/api/l_blob.c rename to src/api/l_data_blob.c diff --git a/src/api/l_modelData.c b/src/api/l_data_modelData.c similarity index 100% rename from src/api/l_modelData.c rename to src/api/l_data_modelData.c diff --git a/src/api/l_rasterizer.c b/src/api/l_data_rasterizer.c similarity index 100% rename from src/api/l_rasterizer.c rename to src/api/l_data_rasterizer.c diff --git a/src/api/l_soundData.c b/src/api/l_data_soundData.c similarity index 100% rename from src/api/l_soundData.c rename to src/api/l_data_soundData.c diff --git a/src/api/l_textureData.c b/src/api/l_data_textureData.c similarity index 100% rename from src/api/l_textureData.c rename to src/api/l_data_textureData.c diff --git a/src/api/l_canvas.c b/src/api/l_graphics_canvas.c similarity index 100% rename from src/api/l_canvas.c rename to src/api/l_graphics_canvas.c diff --git a/src/api/l_font.c b/src/api/l_graphics_font.c similarity index 100% rename from src/api/l_font.c rename to src/api/l_graphics_font.c diff --git a/src/api/l_material.c b/src/api/l_graphics_material.c similarity index 100% rename from src/api/l_material.c rename to src/api/l_graphics_material.c diff --git a/src/api/l_mesh.c b/src/api/l_graphics_mesh.c similarity index 100% rename from src/api/l_mesh.c rename to src/api/l_graphics_mesh.c diff --git a/src/api/l_model.c b/src/api/l_graphics_model.c similarity index 100% rename from src/api/l_model.c rename to src/api/l_graphics_model.c diff --git a/src/api/l_shader.c b/src/api/l_graphics_shader.c similarity index 100% rename from src/api/l_shader.c rename to src/api/l_graphics_shader.c diff --git a/src/api/l_shaderBlock.c b/src/api/l_graphics_shaderBlock.c similarity index 100% rename from src/api/l_shaderBlock.c rename to src/api/l_graphics_shaderBlock.c diff --git a/src/api/l_texture.c b/src/api/l_graphics_texture.c similarity index 100% rename from src/api/l_texture.c rename to src/api/l_graphics_texture.c diff --git a/src/api/l_curve.c b/src/api/l_math_curve.c similarity index 100% rename from src/api/l_curve.c rename to src/api/l_math_curve.c diff --git a/src/api/l_randomGenerator.c b/src/api/l_math_randomGenerator.c similarity index 100% rename from src/api/l_randomGenerator.c rename to src/api/l_math_randomGenerator.c diff --git a/src/api/l_vectors.c b/src/api/l_math_vectors.c similarity index 100% rename from src/api/l_vectors.c rename to src/api/l_math_vectors.c diff --git a/src/api/l_collider.c b/src/api/l_physics_collider.c similarity index 100% rename from src/api/l_collider.c rename to src/api/l_physics_collider.c diff --git a/src/api/l_joints.c b/src/api/l_physics_joints.c similarity index 100% rename from src/api/l_joints.c rename to src/api/l_physics_joints.c diff --git a/src/api/l_shapes.c b/src/api/l_physics_shapes.c similarity index 100% rename from src/api/l_shapes.c rename to src/api/l_physics_shapes.c diff --git a/src/api/l_world.c b/src/api/l_physics_world.c similarity index 100% rename from src/api/l_world.c rename to src/api/l_physics_world.c diff --git a/src/api/l_thread.c b/src/api/l_thread.c index b97482b1..1c454c1c 100644 --- a/src/api/l_thread.c +++ b/src/api/l_thread.c @@ -1,44 +1,108 @@ #include "api.h" +#include "event/event.h" #include "thread/thread.h" +#include "thread/channel.h" +#include "core/ref.h" +#include +#include -static int l_lovrThreadStart(lua_State* L) { - Thread* thread = luax_checktype(L, 1, Thread); - Variant arguments[MAX_THREAD_ARGUMENTS]; - size_t argumentCount = MIN(MAX_THREAD_ARGUMENTS, lua_gettop(L) - 1); - for (size_t i = 0; i < argumentCount; i++) { - luax_checkvariant(L, 2 + i, &arguments[i]); +static int threadRunner(void* data) { + Thread* thread = (Thread*) data; + + lovrRetain(thread); + mtx_lock(&thread->lock); + thread->running = true; + mtx_unlock(&thread->lock); + + lua_State* L = luaL_newstate(); + luaL_openlibs(L); + lovrSetErrorCallback((errorFn*) luax_vthrow, L); + + lua_getglobal(L, "package"); + lua_getfield(L, -1, "preload"); + luaL_register(L, NULL, lovrModules); + lua_pop(L, 2); + + if (!luaL_loadbuffer(L, thread->body->data, thread->body->size, "thread")) { + for (size_t i = 0; i < thread->argumentCount; i++) { + luax_pushvariant(L, &thread->arguments[i]); + } + + if (!lua_pcall(L, thread->argumentCount, 0, 0)) { + mtx_lock(&thread->lock); + thread->running = false; + mtx_unlock(&thread->lock); + lovrRelease(Thread, thread); + lua_close(L); + return 0; + } } - lovrThreadStart(thread, arguments, argumentCount); - return 0; + + // Error handling + size_t length; + const char* error = lua_tolstring(L, -1, &length); + mtx_lock(&thread->lock); + thread->error = malloc(length + 1); + if (thread->error) { + memcpy(thread->error, error, length + 1); + lovrEventPush((Event) { + .type = EVENT_THREAD_ERROR, + .data.thread = { thread, thread->error } + }); + } + thread->running = false; + mtx_unlock(&thread->lock); + lovrRelease(Thread, thread); + lua_close(L); + return 1; } -static int l_lovrThreadWait(lua_State* L) { - Thread* thread = luax_checktype(L, 1, Thread); - lovrThreadWait(thread); - return 0; -} - -static int l_lovrThreadGetError(lua_State* L) { - Thread* thread = luax_checktype(L, 1, Thread); - const char* error = lovrThreadGetError(thread); - if (error) { - lua_pushstring(L, error); +static int l_lovrThreadNewThread(lua_State* L) { + Blob* blob = luax_totype(L, 1, Blob); + if (!blob) { + size_t length; + const char* str = lua_tolstring(L, 1, &length); + if (memchr(str, '\n', MIN(1024, length))) { + void* data = malloc(length + 1); + lovrAssert(data, "Out of memory"); + memcpy(data, str, length + 1); + blob = lovrBlobCreate(data, length, "thread code"); + } else { + void* code = luax_readfile(str, &length); + lovrAssert(code, "Could not read thread code from file '%s'", str); + blob = lovrBlobCreate(code, length, str); + } } else { - lua_pushnil(L); + lovrRetain(blob); } + Thread* thread = lovrThreadCreate(threadRunner, blob); + luax_pushtype(L, Thread, thread); + lovrRelease(Thread, thread); + lovrRelease(Blob, blob); return 1; } -static int l_lovrThreadIsRunning(lua_State* L) { - Thread* thread = luax_checktype(L, 1, Thread); - lua_pushboolean(L, thread->running); +static int l_lovrThreadGetChannel(lua_State* L) { + const char* name = luaL_checkstring(L, 1); + Channel* channel = lovrThreadGetChannel(name); + luax_pushtype(L, Channel, channel); + lovrRelease(Channel, channel); return 1; } -const luaL_Reg lovrThread[] = { - { "start", l_lovrThreadStart }, - { "wait", l_lovrThreadWait }, - { "getError", l_lovrThreadGetError }, - { "isRunning", l_lovrThreadIsRunning }, +static const luaL_Reg lovrThreadModule[] = { + { "newThread", l_lovrThreadNewThread }, + { "getChannel", l_lovrThreadGetChannel }, { NULL, NULL } }; + +int luaopen_lovr_thread(lua_State* L) { + lua_newtable(L); + luaL_register(L, NULL, lovrThreadModule); + luax_registertype(L, Thread); + luax_registertype(L, Channel); + if (lovrThreadModuleInit()) { + luax_atexit(L, lovrThreadModuleDestroy); + } + return 1; +} diff --git a/src/api/l_channel.c b/src/api/l_thread_channel.c similarity index 100% rename from src/api/l_channel.c rename to src/api/l_thread_channel.c diff --git a/src/api/l_thread_module.c b/src/api/l_thread_module.c deleted file mode 100644 index 1c454c1c..00000000 --- a/src/api/l_thread_module.c +++ /dev/null @@ -1,108 +0,0 @@ -#include "api.h" -#include "event/event.h" -#include "thread/thread.h" -#include "thread/channel.h" -#include "core/ref.h" -#include -#include - -static int threadRunner(void* data) { - Thread* thread = (Thread*) data; - - lovrRetain(thread); - mtx_lock(&thread->lock); - thread->running = true; - mtx_unlock(&thread->lock); - - lua_State* L = luaL_newstate(); - luaL_openlibs(L); - lovrSetErrorCallback((errorFn*) luax_vthrow, L); - - lua_getglobal(L, "package"); - lua_getfield(L, -1, "preload"); - luaL_register(L, NULL, lovrModules); - lua_pop(L, 2); - - if (!luaL_loadbuffer(L, thread->body->data, thread->body->size, "thread")) { - for (size_t i = 0; i < thread->argumentCount; i++) { - luax_pushvariant(L, &thread->arguments[i]); - } - - if (!lua_pcall(L, thread->argumentCount, 0, 0)) { - mtx_lock(&thread->lock); - thread->running = false; - mtx_unlock(&thread->lock); - lovrRelease(Thread, thread); - lua_close(L); - return 0; - } - } - - // Error handling - size_t length; - const char* error = lua_tolstring(L, -1, &length); - mtx_lock(&thread->lock); - thread->error = malloc(length + 1); - if (thread->error) { - memcpy(thread->error, error, length + 1); - lovrEventPush((Event) { - .type = EVENT_THREAD_ERROR, - .data.thread = { thread, thread->error } - }); - } - thread->running = false; - mtx_unlock(&thread->lock); - lovrRelease(Thread, thread); - lua_close(L); - return 1; -} - -static int l_lovrThreadNewThread(lua_State* L) { - Blob* blob = luax_totype(L, 1, Blob); - if (!blob) { - size_t length; - const char* str = lua_tolstring(L, 1, &length); - if (memchr(str, '\n', MIN(1024, length))) { - void* data = malloc(length + 1); - lovrAssert(data, "Out of memory"); - memcpy(data, str, length + 1); - blob = lovrBlobCreate(data, length, "thread code"); - } else { - void* code = luax_readfile(str, &length); - lovrAssert(code, "Could not read thread code from file '%s'", str); - blob = lovrBlobCreate(code, length, str); - } - } else { - lovrRetain(blob); - } - Thread* thread = lovrThreadCreate(threadRunner, blob); - luax_pushtype(L, Thread, thread); - lovrRelease(Thread, thread); - lovrRelease(Blob, blob); - return 1; -} - -static int l_lovrThreadGetChannel(lua_State* L) { - const char* name = luaL_checkstring(L, 1); - Channel* channel = lovrThreadGetChannel(name); - luax_pushtype(L, Channel, channel); - lovrRelease(Channel, channel); - return 1; -} - -static const luaL_Reg lovrThreadModule[] = { - { "newThread", l_lovrThreadNewThread }, - { "getChannel", l_lovrThreadGetChannel }, - { NULL, NULL } -}; - -int luaopen_lovr_thread(lua_State* L) { - lua_newtable(L); - luaL_register(L, NULL, lovrThreadModule); - luax_registertype(L, Thread); - luax_registertype(L, Channel); - if (lovrThreadModuleInit()) { - luax_atexit(L, lovrThreadModuleDestroy); - } - return 1; -} diff --git a/src/api/l_thread_thread.c b/src/api/l_thread_thread.c new file mode 100644 index 00000000..b97482b1 --- /dev/null +++ b/src/api/l_thread_thread.c @@ -0,0 +1,44 @@ +#include "api.h" +#include "thread/thread.h" + +static int l_lovrThreadStart(lua_State* L) { + Thread* thread = luax_checktype(L, 1, Thread); + Variant arguments[MAX_THREAD_ARGUMENTS]; + size_t argumentCount = MIN(MAX_THREAD_ARGUMENTS, lua_gettop(L) - 1); + for (size_t i = 0; i < argumentCount; i++) { + luax_checkvariant(L, 2 + i, &arguments[i]); + } + lovrThreadStart(thread, arguments, argumentCount); + return 0; +} + +static int l_lovrThreadWait(lua_State* L) { + Thread* thread = luax_checktype(L, 1, Thread); + lovrThreadWait(thread); + return 0; +} + +static int l_lovrThreadGetError(lua_State* L) { + Thread* thread = luax_checktype(L, 1, Thread); + const char* error = lovrThreadGetError(thread); + if (error) { + lua_pushstring(L, error); + } else { + lua_pushnil(L); + } + return 1; +} + +static int l_lovrThreadIsRunning(lua_State* L) { + Thread* thread = luax_checktype(L, 1, Thread); + lua_pushboolean(L, thread->running); + return 1; +} + +const luaL_Reg lovrThread[] = { + { "start", l_lovrThreadStart }, + { "wait", l_lovrThreadWait }, + { "getError", l_lovrThreadGetError }, + { "isRunning", l_lovrThreadIsRunning }, + { NULL, NULL } +};