#include "../ipc.h" #include "plugin_api.h" #include "lua.h" #include "plugin.h" #include "../../../modules/hashmap/hashmap.h" #include "../../../modules/spng/spng/spng.h" #include #include #include #include #if defined(_WIN32) #define getpid _getpid #else #include #include #endif #define API_VERSION_MAJOR 1 #define API_VERSION_MINOR 0 #define ERRORBUFSIZE 4096 struct FixedBuffer { void* data; size_t size; }; static void set_handler(lua_State* state, const char* regname, uint64_t window_id, int ev) { lua_getfield(state, LUA_REGISTRYINDEX, regname); /*stack: window table*/ \ lua_pushinteger(state, window_id); /*stack: window table, window id*/ \ lua_gettable(state, -2); /*stack: window table, event table*/ \ lua_pushinteger(state, ev); /*stack: window table, event table, event id*/ \ if (lua_isfunction(state, 2)) { \ lua_pushvalue(state, 2); \ } else { \ lua_pushnil(state); \ } /*stack: window table, event table, event id, value*/ \ lua_settable(state, -3); /*stack: window table, event table*/ \ lua_pop(state, 2); /*stack: (empty)*/ \ } #define SETMETATABLE(NAME) \ lua_getfield(state, LUA_REGISTRYINDEX, #NAME "meta"); \ lua_setmetatable(state, -2); #define DEFINE_CALLBACK(APINAME) \ static int api_on##APINAME(lua_State* state) { \ lua_pushliteral(state, #APINAME "cb"); \ if (lua_isfunction(state, 1)) { \ lua_pushvalue(state, 1); \ } else { \ lua_pushnil(state); \ } \ lua_settable(state, LUA_REGISTRYINDEX); \ return 0; \ } #define DEFINE_WINDOWEVENT(APINAME, REGNAME) \ static int api_window_on##APINAME(lua_State* state) { \ const struct EmbeddedWindow* window = require_self_userdata(state, "on" #APINAME); \ set_handler(state, WINDOWS_REGISTRYNAME, window->id, WINDOW_ON##REGNAME); \ return 0; \ } \ DEFINE_BROWSEREVENT(APINAME, REGNAME) #define DEFINE_BROWSEREVENT(APINAME, REGNAME) \ static int api_browser_on##APINAME(lua_State* state) { \ const uint64_t* window_id = require_self_userdata(state, "on" #APINAME); \ set_handler(state, BROWSERS_REGISTRYNAME, *window_id, BROWSER_ON##REGNAME); \ return 0; \ } static void* require_userdata(lua_State* state, int n, const char* apiname) { void* ret = lua_touserdata(state, n); if (!ret) { lua_pushfstring(state, "%s: incorrect argument at position %i, expected userdata", apiname, n); lua_error(state); } return ret; } static void* require_self_userdata(lua_State* state, const char* apiname) { void* ret = lua_touserdata(state, 1); if (!ret) { lua_pushfstring(state, "%s: incorrect first argument, expected userdata (did you mean to use ':' instead of '.'?)", apiname); lua_error(state); } return ret; } static void get_binary_data(lua_State* state, int n, const void** data, size_t* size) { if (lua_isuserdata(state, n)) { const struct FixedBuffer* buffer = lua_touserdata(state, n); *data = buffer->data; *size = buffer->size; } else { *data = luaL_checklstring(state, n, size); } } static int surface_gc(lua_State* state) { const struct PluginManagedFunctions* managed_functions = _bolt_plugin_managed_functions(); const struct SurfaceFunctions* functions = lua_touserdata(state, 1); managed_functions->surface_destroy(functions->userdata); return 0; } static int buffer_gc(lua_State* state) { const struct FixedBuffer* buffer = lua_touserdata(state, 1); free(buffer->data); return 0; } static int shader_gc(lua_State* state) { const struct PluginManagedFunctions* managed_functions = _bolt_plugin_managed_functions(); const struct ShaderFunctions* shader = lua_touserdata(state, 1); managed_functions->shader_destroy(shader->userdata); return 0; } static int shaderprogram_gc(lua_State* state) { const struct PluginManagedFunctions* managed_functions = _bolt_plugin_managed_functions(); const struct ShaderProgramFunctions* program = lua_touserdata(state, 1); managed_functions->shader_program_destroy(program->userdata); return 0; } static int shaderbuffer_gc(lua_State* state) { const struct PluginManagedFunctions* managed_functions = _bolt_plugin_managed_functions(); const struct ShaderBufferFunctions* buffer = lua_touserdata(state, 1); managed_functions->shader_buffer_destroy(buffer->userdata); return 0; } /* macros that generate handlers for Buffer objects */ #define DEFBUFFERINTERNAL(TYPE, FNAME, LUATYPE) \ static TYPE internal_buffer_get##FNAME(lua_State* state, const void* data, size_t data_size, long offset) { \ TYPE ret; \ if (offset < 0 || (offset + sizeof(TYPE)) > data_size) { \ lua_pushfstring(state, "buffer get" #FNAME ": out-of-bounds read (buffer length %lu, reading at %li)", data_size, offset); \ lua_error(state); \ } \ memcpy(&ret, (uint8_t*)data + offset, sizeof ret); \ return ret; \ } \ static void internal_buffer_set##FNAME(lua_State* state, void* data, size_t data_size, long offset, TYPE value) { \ if (offset < 0 || (offset + sizeof(TYPE)) > data_size) { \ lua_pushfstring(state, "buffer set" #FNAME ": out-of-bounds write (buffer length %lu, writing at %li)", data_size, offset); \ lua_error(state); \ } \ memcpy((uint8_t*)data + offset, &value, sizeof value); \ } \ #define DEFBUFFERINTERNAL_1BYTE(TYPE, FNAME, LUATYPE) \ static TYPE internal_buffer_get##FNAME(lua_State* state, const void* data, size_t data_size, long offset) { \ if (offset < 0 || offset >= data_size) { \ lua_pushfstring(state, "buffer get" #FNAME ": out-of-bounds read (buffer length %lu, reading at %li)", data_size, offset); \ lua_error(state); \ } \ return ((TYPE*)data)[offset]; \ } \ static void internal_buffer_set##FNAME(lua_State* state, void* data, size_t data_size, long offset, TYPE value) { \ if (offset < 0 || offset >= data_size) { \ lua_pushfstring(state, "buffer set" #FNAME ": out-of-bounds write (buffer length %lu, writing at %li)", data_size, offset); \ lua_error(state); \ } \ ((TYPE*)data)[offset] = value; \ } #define DEFBUFFERAPI(TYPE, FNAME, LUATYPE) \ static int api_bufferget##FNAME(lua_State* state) { \ size_t data_length; \ const void* data; \ get_binary_data(state, 1, &data, &data_length); \ const long offset = luaL_checklong(state, 2); \ const TYPE ret = internal_buffer_get##FNAME(state, data, data_length, offset); \ lua_push##LUATYPE(state, ret); \ return 1; \ } \ static int api_buffer_get##FNAME(lua_State* state) { \ const struct FixedBuffer* buffer = require_self_userdata(state, "get" #FNAME); \ const long offset = luaL_checklong(state, 2); \ const TYPE ret = internal_buffer_get##FNAME(state, buffer->data, buffer->size, offset); \ lua_push##LUATYPE(state, ret); \ return 1; \ } \ static int api_buffer_set##FNAME(lua_State* state) { \ const struct FixedBuffer* buffer = require_self_userdata(state, "set" #FNAME); \ const long offset = luaL_checklong(state, 2); \ const TYPE value = (TYPE)luaL_check##LUATYPE(state, 3); \ internal_buffer_set##FNAME(state, buffer->data, buffer->size, offset, value); \ return 0; \ } #define DEFBUFFER(TYPE, FNAME, LUATYPE) DEFBUFFERINTERNAL(TYPE, FNAME, LUATYPE) DEFBUFFERAPI(TYPE, FNAME, LUATYPE) #define DEFBUFFER_1BYTE(TYPE, FNAME, LUATYPE) DEFBUFFERINTERNAL_1BYTE(TYPE, FNAME, LUATYPE) DEFBUFFERAPI(TYPE, FNAME, LUATYPE) /* API definitions start here */ DEFINE_CALLBACK(swapbuffers) DEFINE_CALLBACK(render2d) DEFINE_CALLBACK(render3d) DEFINE_CALLBACK(renderparticles) DEFINE_CALLBACK(renderbillboard) DEFINE_CALLBACK(rendericon) DEFINE_CALLBACK(renderbigicon) DEFINE_CALLBACK(minimapterrain) DEFINE_CALLBACK(minimaprender2d) DEFINE_CALLBACK(renderminimap) DEFINE_CALLBACK(rendergameview) DEFINE_CALLBACK(mousemotion) DEFINE_CALLBACK(mousebutton) DEFINE_CALLBACK(mousebuttonup) DEFINE_CALLBACK(scroll) DEFINE_WINDOWEVENT(reposition, REPOSITION) DEFINE_WINDOWEVENT(mousemotion, MOUSEMOTION) DEFINE_WINDOWEVENT(mousebutton, MOUSEBUTTON) DEFINE_WINDOWEVENT(mousebuttonup, MOUSEBUTTONUP) DEFINE_WINDOWEVENT(scroll, SCROLL) DEFINE_WINDOWEVENT(mouseleave, MOUSELEAVE) DEFINE_BROWSEREVENT(closerequest, CLOSEREQUEST) DEFINE_BROWSEREVENT(message, MESSAGE) static int api_apiversion(lua_State* state) { lua_pushnumber(state, API_VERSION_MAJOR); lua_pushnumber(state, API_VERSION_MINOR); return 2; } static int api_checkversion(lua_State* state) { lua_Integer expected_major = luaL_checkinteger(state, 1); lua_Integer expected_minor = luaL_checkinteger(state, 2); if (expected_major != API_VERSION_MAJOR) { lua_pushfstring(state, "checkversion major version mismatch: major version is %u, plugin expects %u", API_VERSION_MAJOR, (unsigned int)expected_major); lua_error(state); } if (expected_minor > API_VERSION_MINOR) { lua_pushfstring(state, "checkversion minor version mismatch: minor version is %u, plugin expects at least %u", API_VERSION_MINOR, (unsigned int)expected_minor); lua_error(state); } return 2; } static int api_close(lua_State* state) { lua_getfield(state, LUA_REGISTRYINDEX, PLUGIN_REGISTRYNAME); const struct Plugin* plugin = lua_touserdata(state, -1); const uint64_t id = plugin->id; lua_pop(state, 1); _bolt_plugin_stop(id); _bolt_plugin_notify_stopped(id); return 0; } static int api_time(lua_State* state) { uint64_t micros; if (_bolt_monotonic_microseconds(µs)) { lua_pushinteger(state, micros); } else { lua_pushnil(state); } return 1; } static int api_datetime(lua_State* state) { const time_t t = time(NULL); const struct tm* time = gmtime(&t); lua_pushinteger(state, time->tm_year + 1900); lua_pushinteger(state, time->tm_mon + 1); lua_pushinteger(state, time->tm_mday); lua_pushinteger(state, time->tm_hour); lua_pushinteger(state, time->tm_min); lua_pushinteger(state, time->tm_sec); return 6; } static int api_weekday(lua_State* state) { const time_t t = time(NULL); const struct tm* time = gmtime(&t); lua_pushinteger(state, time->tm_wday + 1); return 1; } static int api_playerposition(lua_State* state) { int32_t x, y, z; const struct PluginManagedFunctions* managed_functions = _bolt_plugin_managed_functions(); managed_functions->player_position(&x, &y, &z); struct Point3D* point = lua_newuserdata(state, sizeof(struct Point3D)); point->xyzh.ints[0] = x; point->xyzh.ints[1] = y; point->xyzh.ints[2] = z; point->integer = true; SETMETATABLE(point) return 1; } static int api_gamewindowsize(lua_State* state) { int w, h; _bolt_plugin_overlay_size(&w, &h); lua_pushinteger(state, w); lua_pushinteger(state, h); return 2; } static int api_gameviewxywh(lua_State* state) { int x, y, w, h; const struct PluginManagedFunctions* managed_functions = _bolt_plugin_managed_functions(); managed_functions->game_view_rect(&x, &y, &w, &h); lua_pushinteger(state, x); lua_pushinteger(state, y); lua_pushinteger(state, w); lua_pushinteger(state, h); return 4; } static int api_flashwindow(lua_State* state) { const struct PluginManagedFunctions* managed_functions = _bolt_plugin_managed_functions(); managed_functions->flash_window(); return 0; } static int api_isfocused(lua_State* state) { const struct PluginManagedFunctions* managed_functions = _bolt_plugin_managed_functions(); lua_pushboolean(state, managed_functions->window_has_focus()); return 1; } static int api_characterid(lua_State* state) { const char* hash = _bolt_plugin_character_hash(); lua_pushlstring(state, hash, CHARHASHSIZE); return 1; } static int api_loadfile(lua_State* state) { lua_getfield(state, LUA_REGISTRYINDEX, PLUGIN_REGISTRYNAME); const struct Plugin* plugin = lua_touserdata(state, -1); size_t path_length; const char* path = luaL_checklstring(state, 1, &path_length); const size_t full_path_length = plugin->path_length + path_length + 1; char* full_path = lua_newuserdata(state, full_path_length); memcpy(full_path, plugin->path, plugin->path_length); memcpy(full_path + plugin->path_length, path, path_length + 1); for (char* c = full_path + plugin->path_length; *c; c += 1) { if (*c == '\\') *c = '/'; } FILE* f = fopen(full_path, "rb"); if (!f) { lua_pop(state, 2); lua_pushnil(state); return 1; } fseek(f, 0, SEEK_END); const long file_size = ftell(f); fseek(f, 0, SEEK_SET); void* content = lua_newuserdata(state, file_size); if (fread(content, 1, file_size, f) < file_size) { lua_pop(state, 3); lua_pushnil(state); fclose(f); return 1; } fclose(f); lua_pop(state, 3); lua_pushlstring(state, content, file_size); return 1; } static int api_loadconfig(lua_State* state) { lua_getfield(state, LUA_REGISTRYINDEX, PLUGIN_REGISTRYNAME); const struct Plugin* plugin = lua_touserdata(state, -1); size_t path_length; const char* path = luaL_checklstring(state, 1, &path_length); const size_t full_path_length = plugin->config_path_length + path_length + 1; char* full_path = lua_newuserdata(state, full_path_length); memcpy(full_path, plugin->config_path, plugin->config_path_length); memcpy(full_path + plugin->config_path_length, path, path_length + 1); for (char* c = full_path + plugin->config_path_length; *c; c += 1) { if (*c == '\\') *c = '/'; } FILE* f = fopen(full_path, "rb"); if (!f) { lua_pop(state, 2); lua_pushnil(state); return 1; } fseek(f, 0, SEEK_END); const long file_size = ftell(f); fseek(f, 0, SEEK_SET); void* content = lua_newuserdata(state, file_size); if (fread(content, 1, file_size, f) < file_size) { lua_pop(state, 3); lua_pushnil(state); fclose(f); return 1; } fclose(f); lua_pop(state, 3); lua_pushlstring(state, content, file_size); return 1; } static int api_saveconfig(lua_State* state) { size_t path_length, content_length; const void* content; const char* path = luaL_checklstring(state, 1, &path_length); get_binary_data(state, 2, &content, &content_length); lua_getfield(state, LUA_REGISTRYINDEX, PLUGIN_REGISTRYNAME); const struct Plugin* plugin = lua_touserdata(state, -1); const size_t full_path_length = plugin->config_path_length + path_length + 1; char* full_path = lua_newuserdata(state, full_path_length); memcpy(full_path, plugin->config_path, plugin->config_path_length); memcpy(full_path + plugin->config_path_length, path, path_length + 1); for (char* c = full_path + plugin->config_path_length; *c; c += 1) { if (*c == '\\') *c = '/'; } FILE* f = fopen(full_path, "wb"); if (!f) { lua_pop(state, 2); lua_pushboolean(state, false); return 1; } if (fwrite(content, 1, content_length, f) < content_length) { lua_pop(state, 2); lua_pushboolean(state, false); fclose(f); return 1; } fclose(f); lua_pop(state, 2); lua_pushboolean(state, true); return 1; } static int api_createsurface(lua_State* state) { const struct PluginManagedFunctions* managed_functions = _bolt_plugin_managed_functions(); const lua_Integer w = luaL_checkinteger(state, 1); const lua_Integer h = luaL_checkinteger(state, 2); struct SurfaceFunctions* functions = lua_newuserdata(state, sizeof(struct SurfaceFunctions)); managed_functions->surface_init(functions, w, h, NULL); SETMETATABLE(surface) return 1; } static int api_createsurfacefromrgba(lua_State* state) { const struct PluginManagedFunctions* managed_functions = _bolt_plugin_managed_functions(); const lua_Integer w = luaL_checkinteger(state, 1); const lua_Integer h = luaL_checkinteger(state, 2); const size_t req_length = w * h * 4; size_t length; const void* rgba; get_binary_data(state, 3, &rgba, &length); struct SurfaceFunctions* functions = lua_newuserdata(state, sizeof(struct SurfaceFunctions)); if (length >= req_length) { managed_functions->surface_init(functions, w, h, rgba); } else { uint8_t* ud = lua_newuserdata(state, req_length); memcpy(ud, rgba, length); memset(ud + length, 0, req_length - length); managed_functions->surface_init(functions, w, h, ud); lua_pop(state, 1); } SETMETATABLE(surface) return 1; } static int api_createsurfacefrompng(lua_State* state) { const struct PluginManagedFunctions* managed_functions = _bolt_plugin_managed_functions(); const char extension[] = ".png"; size_t rgba_size, path_length; const char* path = luaL_checklstring(state, 1, &path_length); lua_getfield(state, LUA_REGISTRYINDEX, PLUGIN_REGISTRYNAME); const struct Plugin* plugin = lua_touserdata(state, -1); const size_t full_path_length = plugin->path_length + path_length + sizeof(extension); char* full_path = lua_newuserdata(state, full_path_length); memcpy(full_path, plugin->path, plugin->path_length); memcpy(full_path + plugin->path_length, path, path_length + 1); for (char* c = full_path + plugin->path_length; *c; c += 1) { if (*c == '.') *c = '/'; } memcpy(full_path + plugin->path_length + path_length, extension, sizeof(extension)); FILE* f = fopen(full_path, "rb"); if (!f) { printf("createsurfacefrompng: error opening file '%s'\n", (char*)full_path); return 0; } fseek(f, 0, SEEK_END); const long png_size = ftell(f); fseek(f, 0, SEEK_SET); void* png = lua_newuserdata(state, png_size); const size_t read_count = fread(png, 1, png_size, f); fclose(f); if (read_count < png_size) { printf("createsurfacefrompng: error reading file '%s'\n", (char*)full_path); return 0; } #define CALL_SPNG(FUNC, ...) err = FUNC(__VA_ARGS__); if(err){free(rgba);printf("createsurfacefrompng: error decoding file '%s': " #FUNC " returned %i\n",(char*)full_path,err);return 0;} void* rgba = NULL; int err; spng_ctx* spng = spng_ctx_new(0); CALL_SPNG(spng_set_png_buffer, spng, png, png_size); struct spng_ihdr ihdr; CALL_SPNG(spng_get_ihdr, spng, &ihdr) CALL_SPNG(spng_decoded_image_size, spng, SPNG_FMT_RGBA8, &rgba_size) rgba = malloc(rgba_size); CALL_SPNG(spng_decode_image, spng, rgba, rgba_size, SPNG_FMT_RGBA8, 0) spng_ctx_free(spng); lua_pop(state, 3); #undef CALL_SPNG struct SurfaceFunctions* functions = lua_newuserdata(state, sizeof(struct SurfaceFunctions)); managed_functions->surface_init(functions, ihdr.width, ihdr.height, rgba); SETMETATABLE(surface) lua_pushinteger(state, ihdr.width); lua_pushinteger(state, ihdr.height); free(rgba); return 3; } static int api_createwindow(lua_State* state) { const struct PluginManagedFunctions* managed_functions = _bolt_plugin_managed_functions(); lua_getfield(state, LUA_REGISTRYINDEX, PLUGIN_REGISTRYNAME); const struct Plugin* plugin = lua_touserdata(state, -1); lua_pop(state, 1); // push a window onto the stack as the return value, then initialise it struct EmbeddedWindow* window = lua_newuserdata(state, sizeof(struct EmbeddedWindow)); window->is_deleted = false; window->is_browser = false; window->popup_shown = false; window->do_capture = false; window->reposition_mode = false; window->id = _bolt_plugin_new_windowid(); window->plugin_id = plugin->id; window->plugin = state; _bolt_rwlock_init(&window->lock); _bolt_rwlock_init(&window->input_lock); window->metadata.x = luaL_checkinteger(state, 1); window->metadata.y = luaL_checkinteger(state, 2); window->metadata.width = luaL_checkinteger(state, 3); window->metadata.height = luaL_checkinteger(state, 4); window->last_user_action_metadata = window->metadata; if (window->metadata.width < WINDOW_MIN_SIZE) window->metadata.width = WINDOW_MIN_SIZE; if (window->metadata.height < WINDOW_MIN_SIZE) window->metadata.height = WINDOW_MIN_SIZE; memset(&window->input, 0, sizeof(window->input)); managed_functions->surface_init(&window->surface_functions, window->metadata.width, window->metadata.height, NULL); SETMETATABLE(window) // create an event table in the registry for this window lua_getfield(state, LUA_REGISTRYINDEX, WINDOWS_REGISTRYNAME); lua_pushinteger(state, window->id); lua_createtable(state, WINDOW_EVENT_ENUM_SIZE, 0); lua_pushinteger(state, WINDOW_USERDATA); lua_pushvalue(state, -5); lua_settable(state, -3); lua_settable(state, -3); lua_pop(state, 1); // set this window in the hashmap, which is accessible by backends struct WindowInfo* windows = _bolt_plugin_windowinfo(); _bolt_rwlock_lock_write(&windows->lock); hashmap_set(windows->map, &window); _bolt_rwlock_unlock_write(&windows->lock); return 1; } static int api_createbrowser(lua_State* state) { const int w = luaL_checkinteger(state, 1); const int h = luaL_checkinteger(state, 2); size_t url_length; const char* url = luaL_checklstring(state, 3, &url_length); const char* custom_js = NULL; size_t len; if (lua_gettop(state) >= 4 && !lua_isnil(state, 4)) { custom_js = luaL_checklstring(state, 4, &len); } lua_getfield(state, LUA_REGISTRYINDEX, PLUGIN_REGISTRYNAME); struct Plugin* plugin = lua_touserdata(state, -1); lua_pop(state, 1); // push a window onto the stack as the return value, then initialise it struct ExternalBrowser* browser = lua_newuserdata(state, sizeof(struct ExternalBrowser)); browser->id = _bolt_plugin_new_windowid(); browser->plugin_id = plugin->id; browser->plugin = plugin; browser->do_capture = false; browser->capture_id = 0; SETMETATABLE(browser) // create an empty event table in the registry for this window lua_getfield(state, LUA_REGISTRYINDEX, BROWSERS_REGISTRYNAME); lua_pushinteger(state, browser->id); lua_createtable(state, BROWSER_EVENT_ENUM_SIZE, 0); lua_settable(state, -3); lua_pop(state, 1); // send an IPC message to the host to create a browser const int pid = getpid(); const enum BoltIPCMessageTypeToHost msg_type = IPC_MSG_CREATEBROWSER_EXTERNAL; const struct BoltIPCCreateBrowserHeader header = { .plugin_id = plugin->id, .window_id = browser->id, .url_length = url_length, .pid = pid, .w = w, .h = h, .has_custom_js = custom_js != NULL, .custom_js_length = len, }; const uint32_t url_length32 = (uint32_t)url_length; const BoltSocketType fd = _bolt_plugin_fd(); _bolt_ipc_send(fd, &msg_type, sizeof(msg_type)); _bolt_ipc_send(fd, &header, sizeof(header)); _bolt_ipc_send(fd, url, url_length32); if (custom_js) _bolt_ipc_send(fd, custom_js, len); // add to the hashmap hashmap_set(plugin->external_browsers, &browser); return 1; } static int api_createembeddedbrowser(lua_State* state) { const int x = luaL_checkinteger(state, 1); const int y = luaL_checkinteger(state, 2); const int w = luaL_checkinteger(state, 3); const int h = luaL_checkinteger(state, 4); size_t url_length; const char* url = luaL_checklstring(state, 5, &url_length); const char* custom_js = NULL; size_t custom_js_len; if (lua_gettop(state) >= 6 && !lua_isnil(state, 6)) { custom_js = luaL_checklstring(state, 6, &custom_js_len); } const struct PluginManagedFunctions* managed_functions = _bolt_plugin_managed_functions(); lua_getfield(state, LUA_REGISTRYINDEX, PLUGIN_REGISTRYNAME); const struct Plugin* plugin = lua_touserdata(state, -1); lua_pop(state, 1); // push a window onto the stack as the return value, then initialise it struct EmbeddedWindow* window = lua_newuserdata(state, sizeof(struct EmbeddedWindow)); const uint64_t id = _bolt_plugin_new_windowid(); if (!_bolt_plugin_shm_open_inbound(&window->browser_shm, "eb", id)) { lua_pushliteral(state, "createembeddedbrowser: failed to create shm object"); lua_error(state); } window->is_deleted = false; window->is_browser = true; window->popup_shown = false; window->do_capture = false; window->capture_id = 0; window->popup_initialised = false; window->popup_meta.x = 0; window->popup_meta.y = 0; window->reposition_mode = false; window->id = id; window->plugin_id = plugin->id; window->plugin = state; _bolt_rwlock_init(&window->lock); _bolt_rwlock_init(&window->input_lock); window->metadata.x = x; window->metadata.y = y; window->metadata.width = w; window->metadata.height = h; window->last_user_action_metadata = window->metadata; if (window->metadata.width < WINDOW_MIN_SIZE) window->metadata.width = WINDOW_MIN_SIZE; if (window->metadata.height < WINDOW_MIN_SIZE) window->metadata.height = WINDOW_MIN_SIZE; memset(&window->input, 0, sizeof(window->input)); managed_functions->surface_init(&window->surface_functions, window->metadata.width, window->metadata.height, NULL); SETMETATABLE(embeddedbrowser) // create an event table in the registry for this window lua_getfield(state, LUA_REGISTRYINDEX, BROWSERS_REGISTRYNAME); lua_pushinteger(state, window->id); lua_createtable(state, BROWSER_EVENT_ENUM_SIZE, 0); lua_pushinteger(state, BROWSER_USERDATA); lua_pushvalue(state, -5); lua_settable(state, -3); lua_settable(state, -3); lua_pop(state, 1); // send an IPC message to the host to create an OSR browser const int pid = getpid(); const enum BoltIPCMessageTypeToHost msg_type = IPC_MSG_CREATEBROWSER_OSR; const struct BoltIPCCreateBrowserHeader header = { .plugin_id = plugin->id, .window_id = window->id, .url_length = url_length, .pid = pid, .w = window->metadata.width, .h = window->metadata.height, .has_custom_js = custom_js != NULL, .custom_js_length = custom_js_len, }; const uint32_t url_length32 = (uint32_t)url_length; const BoltSocketType fd = _bolt_plugin_fd(); _bolt_ipc_send(fd, &msg_type, sizeof(msg_type)); _bolt_ipc_send(fd, &header, sizeof(header)); _bolt_ipc_send(fd, url, url_length32); if (custom_js) _bolt_ipc_send(fd, custom_js, custom_js_len); // set this window in the hashmap, which is accessible by backends struct WindowInfo* windows = _bolt_plugin_windowinfo(); _bolt_rwlock_lock_write(&windows->lock); hashmap_set(windows->map, &window); _bolt_rwlock_unlock_write(&windows->lock); return 1; } static int api_point(lua_State* state) { struct Point3D* point = lua_newuserdata(state, sizeof(struct Point3D)); point->xyzh.floats[0] = luaL_checknumber(state, 1); point->xyzh.floats[1] = luaL_checknumber(state, 2); point->xyzh.floats[2] = luaL_checknumber(state, 3); point->xyzh.floats[3] = 1.0; point->integer = false; point->homogenous = false; SETMETATABLE(point) return 1; } static int api_createbuffer(lua_State* state) { const long size = luaL_checklong(state, 1); struct FixedBuffer* buffer = lua_newuserdata(state, sizeof(struct FixedBuffer)); buffer->data = calloc(size, 1); if (!buffer->data) { lua_pushfstring(state, "createbuffer: heap error, failed to allocate %i bytes", size); lua_error(state); } buffer->size = size; SETMETATABLE(buffer) return 1; } static int api_createvertexshader(lua_State* state) { const struct PluginManagedFunctions* functions = _bolt_plugin_managed_functions(); size_t len; const char* source = luaL_checklstring(state, 1, &len); if (len >= INT_MAX) { lua_pushfstring(state, "createvertexshader: source is too long (%lu bytes, max is %i)", len, source); lua_error(state); } char buf[ERRORBUFSIZE]; struct ShaderFunctions* shader = lua_newuserdata(state, sizeof(struct ShaderFunctions)); if (!functions->vertex_shader_init(shader, source, len, buf, ERRORBUFSIZE)) { lua_pushfstring(state, "createvertexshader: shader compilation error: %s", buf); lua_error(state); } SETMETATABLE(shader) return 1; } static int api_createfragmentshader(lua_State* state) { const struct PluginManagedFunctions* functions = _bolt_plugin_managed_functions(); size_t len; const char* source = luaL_checklstring(state, 1, &len); if (len >= INT_MAX) { lua_pushfstring(state, "createfragmentshader: source is too long (%lu bytes, max is %i)", len, source); lua_error(state); } char buf[ERRORBUFSIZE]; struct ShaderFunctions* shader = lua_newuserdata(state, sizeof(struct ShaderFunctions)); if (!functions->fragment_shader_init(shader, source, len, buf, ERRORBUFSIZE)) { lua_pushfstring(state, "createfragmentshader: shader compilation error: %s", buf); lua_error(state); } SETMETATABLE(shader) return 1; } static int api_createshaderprogram(lua_State* state) { const struct PluginManagedFunctions* functions = _bolt_plugin_managed_functions(); const struct ShaderFunctions* vertex = require_userdata(state, 1, "createshaderprogram"); const struct ShaderFunctions* fragment = require_userdata(state, 2, "createshaderprogram"); struct ShaderProgramFunctions* program = lua_newuserdata(state, sizeof(struct ShaderProgramFunctions)); SETMETATABLE(shaderprogram) char buf[ERRORBUFSIZE]; if (!functions->shader_program_init(program, vertex->userdata, fragment->userdata, buf, ERRORBUFSIZE)) { lua_pushfstring(state, "createshaderprogram: link error: %s", buf); lua_error(state); } return 1; } static int api_createshaderbuffer(lua_State* state) { const struct PluginManagedFunctions* functions = _bolt_plugin_managed_functions(); size_t length; const void* data; get_binary_data(state, 1, &data, &length); struct ShaderBufferFunctions* buffer = lua_newuserdata(state, sizeof(struct ShaderBufferFunctions)); functions->shader_buffer_init(buffer, data, length); SETMETATABLE(shaderbuffer) return 1; } static int api_batch2d_vertexcount(lua_State* state) { const struct RenderBatch2D* batch = require_self_userdata(state, "vertexcount"); lua_pushinteger(state, batch->index_count); return 1; } static int api_batch2d_verticesperimage(lua_State* state) { const struct RenderBatch2D* batch = require_self_userdata(state, "verticesperimage"); lua_pushinteger(state, batch->vertices_per_icon); return 1; } static int api_batch2d_targetsize(lua_State* state) { const struct RenderBatch2D* batch = require_self_userdata(state, "targetsize"); lua_pushinteger(state, batch->screen_width); lua_pushinteger(state, batch->screen_height); return 2; } static int api_batch2d_vertexxy(lua_State* state) { const struct RenderBatch2D* batch = require_self_userdata(state, "vertexxy"); const lua_Integer index = luaL_checkinteger(state, 2); int32_t xy[2]; batch->vertex_functions.xy(index - 1, batch->vertex_functions.userdata, xy); lua_pushinteger(state, xy[0]); lua_pushinteger(state, xy[1]); return 2; } static int api_batch2d_vertexatlasdetails(lua_State* state) { const struct RenderBatch2D* batch = require_self_userdata(state, "vertexatlasxy"); const lua_Integer index = luaL_checkinteger(state, 2); int32_t xywh[4]; uint8_t wrapx, wrapy; batch->vertex_functions.atlas_details(index - 1, batch->vertex_functions.userdata, xywh, &wrapx, &wrapy); lua_pushnumber(state, xywh[0]); lua_pushnumber(state, xywh[1]); lua_pushnumber(state, xywh[2]); lua_pushnumber(state, xywh[3]); lua_pushboolean(state, wrapx); lua_pushboolean(state, wrapy); return 6; } static int api_batch2d_vertexuv(lua_State* state) { const struct RenderBatch2D* batch = require_self_userdata(state, "vertexuv"); const lua_Integer index = lua_tointeger(state, 2); double uv[2]; uint8_t discard; batch->vertex_functions.uv(index - 1, batch->vertex_functions.userdata, uv, &discard); if (discard) { lua_pushnil(state); lua_pushnil(state); } else { lua_pushnumber(state, uv[0]); lua_pushnumber(state, uv[1]); } return 2; } static int api_batch2d_vertexcolour(lua_State* state) { const struct RenderBatch2D* batch = require_self_userdata(state, "vertexcolour"); const lua_Integer index = luaL_checkinteger(state, 2); double colour[4]; batch->vertex_functions.colour(index - 1, batch->vertex_functions.userdata, colour); lua_pushnumber(state, colour[0]); lua_pushnumber(state, colour[1]); lua_pushnumber(state, colour[2]); lua_pushnumber(state, colour[3]); return 4; } static int api_batch2d_textureid(lua_State* state) { const struct RenderBatch2D* render = require_self_userdata(state, "textureid"); const size_t id = render->texture_functions.id(render->texture_functions.userdata); lua_pushinteger(state, id); return 1; } static int api_batch2d_texturesize(lua_State* state) { const struct RenderBatch2D* render = require_self_userdata(state, "texturesize"); size_t size[2]; render->texture_functions.size(render->texture_functions.userdata, size); lua_pushinteger(state, size[0]); lua_pushinteger(state, size[1]); return 2; } static int api_batch2d_texturecompare(lua_State* state) { const struct RenderBatch2D* render = require_self_userdata(state, "texturecompare"); const size_t x = luaL_checkinteger(state, 2); const size_t y = luaL_checkinteger(state, 3); size_t data_len; const void* data; get_binary_data(state, 4, &data, &data_len); const uint8_t match = render->texture_functions.compare(render->texture_functions.userdata, x, y, data_len, (const unsigned char*)data); lua_pushboolean(state, match); return 1; } static int api_batch2d_texturedata(lua_State* state) { const struct RenderBatch2D* render = require_self_userdata(state, "texturedata"); const size_t x = luaL_checkinteger(state, 2); const size_t y = luaL_checkinteger(state, 3); const size_t len = luaL_checkinteger(state, 4); const uint8_t* ret = render->texture_functions.data(render->texture_functions.userdata, x, y); lua_pushlstring(state, (const char*)ret, len); return 1; } static int api_minimapterrain_angle(lua_State* state) { const struct MinimapTerrainEvent* render = require_self_userdata(state, "angle"); lua_pushnumber(state, render->angle); return 1; } static int api_minimapterrain_scale(lua_State* state) { const struct MinimapTerrainEvent* render = require_self_userdata(state, "scale"); lua_pushnumber(state, render->scale); return 1; } static int api_minimapterrain_position(lua_State* state) { const struct MinimapTerrainEvent* render = require_self_userdata(state, "position"); lua_pushnumber(state, render->x); lua_pushnumber(state, render->y); return 2; } static int api_renderminimap_sourcexywh(lua_State* state) { const struct RenderMinimapEvent* render = require_self_userdata(state, "sourcexywh"); lua_pushinteger(state, render->source_x); lua_pushinteger(state, render->source_y); lua_pushinteger(state, render->source_w); lua_pushinteger(state, render->source_h); return 4; } static int api_renderminimap_targetxywh(lua_State* state) { const struct RenderMinimapEvent* render = require_self_userdata(state, "targetxywh"); lua_pushinteger(state, render->target_x); lua_pushinteger(state, render->target_y); lua_pushinteger(state, render->target_w); lua_pushinteger(state, render->target_h); return 4; } static int api_renderminimap_targetsize(lua_State* state) { const struct RenderMinimapEvent* render = require_self_userdata(state, "targetsize"); lua_pushinteger(state, render->screen_width); lua_pushinteger(state, render->screen_height); return 2; } static int api_rendergameview_size(lua_State* state) { const struct RenderGameViewEvent* event = require_self_userdata(state, "size"); int w, h; event->functions.size(event->functions.userdata, &w, &h); lua_pushinteger(state, w); lua_pushinteger(state, h); return 2; } static int api_point_transform(lua_State* state) { const struct Point3D* point = require_self_userdata(state, "transform"); const struct Transform3D* transform = require_userdata(state, 2, "transform"); double x, y, z, h; if (point->integer) { x = (double)point->xyzh.ints[0]; y = (double)point->xyzh.ints[1]; z = (double)point->xyzh.ints[2]; h = 1.0; } else { x = point->xyzh.floats[0]; y = point->xyzh.floats[1]; z = point->xyzh.floats[2]; h = point->xyzh.floats[3]; } struct Point3D* out = lua_newuserdata(state, sizeof(struct Point3D)); out->xyzh.floats[0] = (x * transform->matrix[0]) + (y * transform->matrix[4]) + (z * transform->matrix[8]) + (h * transform->matrix[12]); out->xyzh.floats[1] = (x * transform->matrix[1]) + (y * transform->matrix[5]) + (z * transform->matrix[9]) + (h * transform->matrix[13]); out->xyzh.floats[2] = (x * transform->matrix[2]) + (y * transform->matrix[6]) + (z * transform->matrix[10]) + (h * transform->matrix[14]); out->xyzh.floats[3] = (x * transform->matrix[3]) + (y * transform->matrix[7]) + (z * transform->matrix[11]) + (h * transform->matrix[15]); out->integer = false; out->homogenous = true; SETMETATABLE(point) return 1; } static int api_point_get(lua_State* state) { const struct Point3D* point = require_self_userdata(state, "get"); if (point->integer) { lua_pushinteger(state, point->xyzh.ints[0]); lua_pushinteger(state, point->xyzh.ints[1]); lua_pushinteger(state, point->xyzh.ints[2]); } else if (!point->homogenous) { lua_pushnumber(state, point->xyzh.floats[0]); lua_pushnumber(state, point->xyzh.floats[1]); lua_pushnumber(state, point->xyzh.floats[2]); } else { lua_pushnumber(state, point->xyzh.floats[0] / point->xyzh.floats[3]); lua_pushnumber(state, point->xyzh.floats[1] / point->xyzh.floats[3]); lua_pushnumber(state, point->xyzh.floats[2] / point->xyzh.floats[3]); } return 3; } static int api_point_toscreen(lua_State* state) { const struct Point3D* point = require_self_userdata(state, "toscreen"); int game_view_x, game_view_y, game_view_w, game_view_h; const struct PluginManagedFunctions* managed_functions = _bolt_plugin_managed_functions(); managed_functions->game_view_rect(&game_view_x, &game_view_y, &game_view_w, &game_view_h); double x, y, depth; if (point->integer) { x = (double)point->xyzh.ints[0]; y = (double)point->xyzh.ints[1]; depth = (double)point->xyzh.ints[2]; } else if (!point->homogenous) { x = point->xyzh.floats[0]; y = point->xyzh.floats[1]; depth = point->xyzh.floats[2]; } else { x = point->xyzh.floats[0] / point->xyzh.floats[3]; y = point->xyzh.floats[1] / point->xyzh.floats[3]; depth = point->xyzh.floats[2] / point->xyzh.floats[3]; } lua_pushnumber(state, ((x + 1.0) * game_view_w / 2.0) + (double)game_view_x); lua_pushnumber(state, ((-y + 1.0) * game_view_h / 2.0) + (double)game_view_y); lua_pushnumber(state, depth); return 3; } static int api_point_togameview(lua_State* state) { const struct Point3D* point = require_self_userdata(state, "togameview"); int game_view_x, game_view_y, game_view_w, game_view_h; const struct PluginManagedFunctions* managed_functions = _bolt_plugin_managed_functions(); managed_functions->game_view_rect(&game_view_x, &game_view_y, &game_view_w, &game_view_h); double x, y, depth; if (point->integer) { x = (double)point->xyzh.ints[0]; y = (double)point->xyzh.ints[1]; depth = (double)point->xyzh.ints[2]; } else if (!point->homogenous) { x = point->xyzh.floats[0]; y = point->xyzh.floats[1]; depth = point->xyzh.floats[2]; } else { x = point->xyzh.floats[0] / point->xyzh.floats[3]; y = point->xyzh.floats[1] / point->xyzh.floats[3]; depth = point->xyzh.floats[2] / point->xyzh.floats[3]; } lua_pushnumber(state, ((x + 1.0) * game_view_w / 2.0)); lua_pushnumber(state, ((-y + 1.0) * game_view_h / 2.0)); lua_pushnumber(state, depth); return 3; } #define LENGTH(N1, N2, N3) sqrt((N1 * N1) + (N2 * N2) + (N3 * N3)) static int api_transform_decompose(lua_State* state) { const struct Transform3D* transform = require_self_userdata(state, "decompose"); lua_pushnumber(state, transform->matrix[12]); lua_pushnumber(state, transform->matrix[13]); lua_pushnumber(state, transform->matrix[14]); const double scale_x = LENGTH(transform->matrix[0], transform->matrix[1], transform->matrix[2]); const double scale_y = LENGTH(transform->matrix[4], transform->matrix[5], transform->matrix[6]); const double scale_z = LENGTH(transform->matrix[8], transform->matrix[9], transform->matrix[10]); lua_pushnumber(state, scale_x); lua_pushnumber(state, scale_y); lua_pushnumber(state, scale_z); if (scale_x == 0.0 || scale_y == 0.0 || scale_z == 0.0) { // scale=0 means we can't calculate angles because there would be a division by zero, // and angle is meaningless for a zero-scale model anyway, so just put 0 for all the angles lua_pushnumber(state, 0.0); lua_pushnumber(state, 0.0); lua_pushnumber(state, 0.0); } else { // see https://stackoverflow.com/questions/39128589/decomposing-rotation-matrix-x-y-z-cartesian-angles const double yaw = atan2(-(transform->matrix[8] / scale_z), sqrt((transform->matrix[0] * transform->matrix[0] / (scale_x * scale_x)) + (transform->matrix[4] * transform->matrix[4] / (scale_y * scale_y)))); const double pitch = atan2((transform->matrix[9] / scale_z) / cos(yaw), (transform->matrix[10] / scale_z) / cos(yaw)); const double roll = atan2((transform->matrix[4] / scale_y) / cos(yaw), (transform->matrix[0] / scale_x) / cos(yaw)); lua_pushnumber(state, yaw); lua_pushnumber(state, pitch); lua_pushnumber(state, roll); } return 9; } #undef LENGTH static int api_transform_get(lua_State* state) { const struct Transform3D* transform = require_self_userdata(state, "get"); for (size_t i = 0; i < 16; i += 1) { lua_pushnumber(state, transform->matrix[i]); } return 16; } static int api_surface_clear(lua_State* state) { const struct SurfaceFunctions* functions = require_self_userdata(state, "clear"); const int argc = lua_gettop(state); const double r = (argc >= 2) ? luaL_checknumber(state, 2) : 0.0; const double g = (argc >= 3) ? luaL_checknumber(state, 3) : r; const double b = (argc >= 4) ? luaL_checknumber(state, 4) : r; const double a = (argc >= 5) ? luaL_checknumber(state, 5) : ((argc > 1) ? 1.0 : 0.0); functions->clear(functions->userdata, r, g, b, a); return 0; } static int api_surface_subimage(lua_State* state) { const struct SurfaceFunctions* functions = require_self_userdata(state, "subimage"); const lua_Integer x = luaL_checkinteger(state, 2); const lua_Integer y = luaL_checkinteger(state, 3); const lua_Integer w = luaL_checkinteger(state, 4); const lua_Integer h = luaL_checkinteger(state, 5); const size_t req_length = w * h * 4; size_t length; const void* rgba; get_binary_data(state, 6, &rgba, &length); if (length >= req_length) { functions->subimage(functions->userdata, x, y, w, h, rgba, 0); } else { uint8_t* ud = lua_newuserdata(state, req_length); memcpy(ud, rgba, length); memset(ud + length, 0, req_length - length); functions->subimage(functions->userdata, x, y, w, h, (const void*)ud, 0); lua_pop(state, 1); } return 0; } static int api_surface_drawtoscreen(lua_State* state) { const struct SurfaceFunctions* functions = require_self_userdata(state, "drawtoscreen"); const int sx = luaL_checkinteger(state, 2); const int sy = luaL_checkinteger(state, 3); const int sw = luaL_checkinteger(state, 4); const int sh = luaL_checkinteger(state, 5); const int dx = luaL_checkinteger(state, 6); const int dy = luaL_checkinteger(state, 7); const int dw = luaL_checkinteger(state, 8); const int dh = luaL_checkinteger(state, 9); _bolt_plugin_draw_to_overlay(functions, sx, sy, sw, sh, dx, dy, dw, dh); return 0; } static int api_surface_drawtosurface(lua_State* state) { const struct SurfaceFunctions* functions = require_self_userdata(state, "drawtosurface"); const struct SurfaceFunctions* target = require_userdata(state, 2, "drawtosurface"); const int sx = luaL_checkinteger(state, 3); const int sy = luaL_checkinteger(state, 4); const int sw = luaL_checkinteger(state, 5); const int sh = luaL_checkinteger(state, 6); const int dx = luaL_checkinteger(state, 7); const int dy = luaL_checkinteger(state, 8); const int dw = luaL_checkinteger(state, 9); const int dh = luaL_checkinteger(state, 10); functions->draw_to_surface(functions->userdata, target->userdata, sx, sy, sw, sh, dx, dy, dw, dh); return 0; } static int api_surface_drawtogameview(lua_State* state) { const struct SurfaceFunctions* functions = require_self_userdata(state, "drawtogameview"); const struct RenderGameViewEvent* gameview = require_userdata(state, 2, "drawtogameview"); const int sx = luaL_checkinteger(state, 3); const int sy = luaL_checkinteger(state, 4); const int sw = luaL_checkinteger(state, 5); const int sh = luaL_checkinteger(state, 6); const int dx = luaL_checkinteger(state, 7); const int dy = luaL_checkinteger(state, 8); const int dw = luaL_checkinteger(state, 9); const int dh = luaL_checkinteger(state, 10); functions->draw_to_gameview(functions->userdata, gameview->functions.userdata, sx, sy, sw, sh, dx, dy, dw, dh); return 0; } static int api_surface_drawtowindow(lua_State* state) { const struct SurfaceFunctions* functions = require_self_userdata(state, "drawtowindow"); const struct EmbeddedWindow* target = require_userdata(state, 2, "drawtowindow"); const int sx = luaL_checkinteger(state, 3); const int sy = luaL_checkinteger(state, 4); const int sw = luaL_checkinteger(state, 5); const int sh = luaL_checkinteger(state, 6); const int dx = luaL_checkinteger(state, 7); const int dy = luaL_checkinteger(state, 8); const int dw = luaL_checkinteger(state, 9); const int dh = luaL_checkinteger(state, 10); functions->draw_to_surface(functions->userdata, target->surface_functions.userdata, sx, sy, sw, sh, dx, dy, dw, dh); return 0; } static int api_surface_settint(lua_State* state) { const struct SurfaceFunctions* functions = require_self_userdata(state, "settint"); const lua_Number r = luaL_checknumber(state, 2); const lua_Number g = luaL_checknumber(state, 3); const lua_Number b = luaL_checknumber(state, 4); functions->set_tint(functions->userdata, r, g, b); return 0; } static int api_surface_setalpha(lua_State* state) { const struct SurfaceFunctions* functions = require_self_userdata(state, "setalpha"); const lua_Number alpha = luaL_checknumber(state, 2); functions->set_alpha(functions->userdata, alpha); return 0; } static int api_window_close(lua_State* state) { struct EmbeddedWindow* window = require_self_userdata(state, "state"); window->is_deleted = true; return 0; } static int api_window_clear(lua_State* state) { const struct EmbeddedWindow* window = require_self_userdata(state, "clear"); const int argc = lua_gettop(state); const double r = (argc >= 2) ? luaL_checknumber(state, 2) : 0.0; const double g = (argc >= 3) ? luaL_checknumber(state, 3) : r; const double b = (argc >= 4) ? luaL_checknumber(state, 4) : r; const double a = (argc >= 5) ? luaL_checknumber(state, 5) : ((argc > 1) ? 1.0 : 0.0); window->surface_functions.clear(window->surface_functions.userdata, r, g, b, a); return 0; } static int api_window_id(lua_State* state) { const struct EmbeddedWindow* window = require_self_userdata(state, "id"); lua_pushinteger(state, window->id); return 1; } static int api_window_subimage(lua_State* state) { const struct EmbeddedWindow* window = require_self_userdata(state, "subimage"); const lua_Integer x = luaL_checkinteger(state, 2); const lua_Integer y = luaL_checkinteger(state, 3); const lua_Integer w = luaL_checkinteger(state, 4); const lua_Integer h = luaL_checkinteger(state, 5); const size_t req_length = w * h * 4; size_t length; const void* rgba; get_binary_data(state, 6, &rgba, &length); if (length >= req_length) { window->surface_functions.subimage(window->surface_functions.userdata, x, y, w, h, rgba, 0); } else { uint8_t* ud = lua_newuserdata(state, req_length); memcpy(ud, rgba, length); memset(ud + length, 0, req_length - length); window->surface_functions.subimage(window->surface_functions.userdata, x, y, w, h, (const void*)ud, 0); lua_pop(state, 1); } return 0; } static int api_window_startreposition(lua_State* state) { struct EmbeddedWindow* window = require_self_userdata(state, "startreposition"); const lua_Integer xscale = luaL_checkinteger(state, 2); const lua_Integer yscale = luaL_checkinteger(state, 3); window->reposition_mode = true; window->reposition_threshold = false; window->reposition_w = (xscale == 0) ? 0 : ((xscale > 0) ? 1 : -1); window->reposition_h = (yscale == 0) ? 0 : ((yscale > 0) ? 1 : -1); return 0; } static int api_window_cancelreposition(lua_State* state) { struct EmbeddedWindow* window = require_self_userdata(state, "cancelreposition"); window->reposition_mode = false; return 0; } static int api_window_xywh(lua_State* state) { struct EmbeddedWindow* window = require_self_userdata(state, "xywh"); _bolt_rwlock_lock_read(&window->lock); lua_pushinteger(state, window->metadata.x); lua_pushinteger(state, window->metadata.y); lua_pushinteger(state, window->metadata.width); lua_pushinteger(state, window->metadata.height); _bolt_rwlock_unlock_read(&window->lock); return 4; } static int api_render3d_vertexcount(lua_State* state) { const struct Render3D* render = require_self_userdata(state, "vertexcount"); lua_pushinteger(state, render->vertex_count); return 1; } static int api_render3d_vertexpoint(lua_State* state) { const struct Render3D* render = require_self_userdata(state, "vertexpoint"); const lua_Integer index = lua_tointeger(state, 2); struct Point3D* point = lua_newuserdata(state, sizeof(struct Point3D)); render->vertex_functions.xyz(index - 1, render->vertex_functions.userdata, point); SETMETATABLE(point) return 1; } static int api_render3d_modelmatrix(lua_State* state) { const struct Render3D* render = require_self_userdata(state, "modelmatrix"); struct Transform3D* transform = lua_newuserdata(state, sizeof(struct Transform3D)); render->matrix_functions.model_matrix(render->matrix_functions.userdata, transform); SETMETATABLE(transform) return 1; } static int api_render3d_viewmatrix(lua_State* state) { const struct Render3D* render = require_self_userdata(state, "viewmatrix"); struct Transform3D* transform = lua_newuserdata(state, sizeof(struct Transform3D)); render->matrix_functions.view_matrix(render->matrix_functions.userdata, transform); SETMETATABLE(transform) return 1; } static int api_render3d_projmatrix(lua_State* state) { const struct Render3D* render = require_self_userdata(state, "projmatrix"); struct Transform3D* transform = lua_newuserdata(state, sizeof(struct Transform3D)); render->matrix_functions.proj_matrix(render->matrix_functions.userdata, transform); SETMETATABLE(transform) return 1; } static int api_render3d_viewprojmatrix(lua_State* state) { const struct Render3D* render = require_self_userdata(state, "viewprojmatrix"); struct Transform3D* transform = lua_newuserdata(state, sizeof(struct Transform3D)); render->matrix_functions.viewproj_matrix(render->matrix_functions.userdata, transform); SETMETATABLE(transform) return 1; } static int api_render3d_inverseviewmatrix(lua_State* state) { const struct Render3D* render = require_self_userdata(state, "inverseviewmatrix"); struct Transform3D* transform = lua_newuserdata(state, sizeof(struct Transform3D)); render->matrix_functions.inverse_view_matrix(render->matrix_functions.userdata, transform); SETMETATABLE(transform) return 1; } static int api_render3d_vertexmeta(lua_State* state) { const struct Render3D* render = require_self_userdata(state, "vertexmeta"); const lua_Integer index = luaL_checkinteger(state, 2); const size_t meta = render->vertex_functions.atlas_meta(index - 1, render->vertex_functions.userdata); lua_pushinteger(state, meta); return 1; } static int api_render3d_atlasxywh(lua_State* state) { const struct Render3D* render = require_self_userdata(state, "atlasxywh"); const lua_Integer meta = luaL_checkinteger(state, 2); int32_t xywh[4]; render->vertex_functions.atlas_xywh(meta, render->vertex_functions.userdata, xywh); lua_pushinteger(state, xywh[0]); lua_pushinteger(state, xywh[1]); lua_pushinteger(state, xywh[2]); lua_pushinteger(state, xywh[3]); return 4; } static int api_render3d_vertexuv(lua_State* state) { const struct Render3D* render = require_self_userdata(state, "vertexuv"); const lua_Integer index = luaL_checkinteger(state, 2); double uv[4]; render->vertex_functions.uv(index - 1, render->vertex_functions.userdata, uv); lua_pushnumber(state, uv[0]); lua_pushnumber(state, uv[1]); return 2; } static int api_render3d_vertexcolour(lua_State* state) { const struct Render3D* render = require_self_userdata(state, "vertexcolour"); const lua_Integer index = luaL_checkinteger(state, 2); double col[4]; render->vertex_functions.colour(index - 1, render->vertex_functions.userdata, col); lua_pushnumber(state, col[0]); lua_pushnumber(state, col[1]); lua_pushnumber(state, col[2]); lua_pushnumber(state, col[3]); return 4; } static int api_render3d_textureid(lua_State* state) { const struct Render3D* render = require_self_userdata(state, "textureid"); const size_t id = render->texture_functions.id(render->texture_functions.userdata); lua_pushinteger(state, id); return 1; } static int api_render3d_texturesize(lua_State* state) { const struct Render3D* render = require_self_userdata(state, "texturesize"); size_t size[2]; render->texture_functions.size(render->texture_functions.userdata, size); lua_pushinteger(state, size[0]); lua_pushinteger(state, size[1]); return 2; } static int api_render3d_texturecompare(lua_State* state) { const struct Render3D* render = require_self_userdata(state, "texturecompare"); const size_t x = luaL_checkinteger(state, 2); const size_t y = luaL_checkinteger(state, 3); size_t data_len; const void* data; get_binary_data(state, 4, &data, &data_len); const uint8_t match = render->texture_functions.compare(render->texture_functions.userdata, x, y, data_len, (const unsigned char*)data); lua_pushboolean(state, match); return 1; } static int api_render3d_texturedata(lua_State* state) { const struct Render3D* render = require_self_userdata(state, "texturedata"); const size_t x = luaL_checkinteger(state, 2); const size_t y = luaL_checkinteger(state, 3); const size_t len = luaL_checkinteger(state, 4); const uint8_t* ret = render->texture_functions.data(render->texture_functions.userdata, x, y); lua_pushlstring(state, (const char*)ret, len); return 1; } static int api_render3d_vertexanimation(lua_State* state) { const struct Render3D* render = require_self_userdata(state, "vertexanimation"); const lua_Integer vertex = luaL_checkinteger(state, 2); if (!render->is_animated) { lua_pushliteral(state, "boneanimation: cannot get bone transforms for non-animated model"); lua_error(state); } struct Transform3D* transform = lua_newuserdata(state, sizeof(struct Transform3D)); render->vertex_functions.bone_transform(vertex - 1, render->vertex_functions.userdata, transform); SETMETATABLE(transform) return 1; } static int api_render3d_animated(lua_State* state) { const struct Render3D* render = require_self_userdata(state, "animated"); lua_pushboolean(state, render->is_animated); return 1; } static int api_render3d_cameraposition(lua_State* state) { const struct Render3D* render = require_self_userdata(state, "cameraposition"); double out[3]; render->camera_functions.position(render->camera_functions.userdata, out); lua_pushnumber(state, out[0]); lua_pushnumber(state, out[1]); lua_pushnumber(state, out[2]); return 3; } static int api_renderparticles_vertexcount(lua_State* state) { const struct RenderParticles* render = require_self_userdata(state, "vertexcount"); lua_pushinteger(state, render->vertex_count); return 1; } static int api_renderparticles_vertexparticleorigin(lua_State* state) { const struct RenderParticles* render = require_self_userdata(state, "vertexparticleorigin"); const lua_Integer vertex = luaL_checkinteger(state, 2); struct Point3D* point = lua_newuserdata(state, sizeof(struct Point3D)); render->vertex_functions.xyz(vertex - 1, render->vertex_functions.userdata, point); SETMETATABLE(point) return 1; } static int api_renderparticles_vertexworldoffset(lua_State* state) { const struct RenderParticles* render = require_self_userdata(state, "vertexworldoffset"); const lua_Integer vertex = luaL_checkinteger(state, 2); double offset[3]; render->vertex_functions.world_offset(vertex - 1, render->vertex_functions.userdata, offset); lua_pushnumber(state, offset[0]); lua_pushnumber(state, offset[1]); lua_pushnumber(state, offset[2]); return 3; } static int api_renderparticles_vertexeyeoffset(lua_State* state) { const struct RenderParticles* render = require_self_userdata(state, "vertexeyeoffset"); const lua_Integer vertex = luaL_checkinteger(state, 2); double offset[2]; render->vertex_functions.eye_offset(vertex - 1, render->vertex_functions.userdata, offset); lua_pushnumber(state, offset[0]); lua_pushnumber(state, offset[1]); return 2; } static int api_renderparticles_vertexuv(lua_State* state) { const struct RenderParticles* render = require_self_userdata(state, "vertexuv"); const lua_Integer vertex = luaL_checkinteger(state, 2); double uv[2]; render->vertex_functions.uv(vertex - 1, render->vertex_functions.userdata, uv); lua_pushnumber(state, uv[0]); lua_pushnumber(state, uv[1]); return 2; } static int api_renderparticles_vertexcolour(lua_State* state) { const struct RenderParticles* render = require_self_userdata(state, "vertexcolour"); const lua_Integer index = luaL_checkinteger(state, 2); double col[4]; render->vertex_functions.colour(index - 1, render->vertex_functions.userdata, col); lua_pushnumber(state, col[0]); lua_pushnumber(state, col[1]); lua_pushnumber(state, col[2]); lua_pushnumber(state, col[3]); return 4; } static int api_renderparticles_vertexmeta(lua_State* state) { const struct RenderParticles* render = require_self_userdata(state, "vertexmeta"); const lua_Integer index = luaL_checkinteger(state, 2); const size_t meta = render->vertex_functions.atlas_meta(index - 1, render->vertex_functions.userdata); lua_pushinteger(state, meta); return 1; } static int api_renderparticles_atlasxywh(lua_State* state) { const struct RenderParticles* render = require_self_userdata(state, "atlasxywh"); const lua_Integer meta = luaL_checkinteger(state, 2); int32_t xywh[4]; render->vertex_functions.atlas_xywh(meta, render->vertex_functions.userdata, xywh); lua_pushinteger(state, xywh[0]); lua_pushinteger(state, xywh[1]); lua_pushinteger(state, xywh[2]); lua_pushinteger(state, xywh[3]); return 4; } static int api_renderparticles_textureid(lua_State* state) { const struct RenderParticles* render = require_self_userdata(state, "textureid"); const size_t id = render->texture_functions.id(render->texture_functions.userdata); lua_pushinteger(state, id); return 1; } static int api_renderparticles_texturesize(lua_State* state) { const struct RenderParticles* render = require_self_userdata(state, "texturesize"); size_t size[2]; render->texture_functions.size(render->texture_functions.userdata, size); lua_pushinteger(state, size[0]); lua_pushinteger(state, size[1]); return 2; } static int api_renderparticles_texturecompare(lua_State* state) { const struct RenderParticles* render = require_self_userdata(state, "texturecompare"); const size_t x = luaL_checkinteger(state, 2); const size_t y = luaL_checkinteger(state, 3); size_t data_len; const void* data; get_binary_data(state, 4, &data, &data_len); const uint8_t match = render->texture_functions.compare(render->texture_functions.userdata, x, y, data_len, (const unsigned char*)data); lua_pushboolean(state, match); return 1; } static int api_renderparticles_texturedata(lua_State* state) { const struct RenderParticles* render = require_self_userdata(state, "texturedata"); const size_t x = luaL_checkinteger(state, 2); const size_t y = luaL_checkinteger(state, 3); const size_t len = luaL_checkinteger(state, 4); const uint8_t* ret = render->texture_functions.data(render->texture_functions.userdata, x, y); lua_pushlstring(state, (const char*)ret, len); return 1; } static int api_renderparticles_viewmatrix(lua_State* state) { const struct RenderParticles* render = require_self_userdata(state, "viewmatrix"); struct Transform3D* transform = lua_newuserdata(state, sizeof(struct Transform3D)); render->matrix_functions.view_matrix(render->matrix_functions.userdata, transform); SETMETATABLE(transform) return 1; } static int api_renderparticles_projmatrix(lua_State* state) { const struct RenderParticles* render = require_self_userdata(state, "projmatrix"); struct Transform3D* transform = lua_newuserdata(state, sizeof(struct Transform3D)); render->matrix_functions.proj_matrix(render->matrix_functions.userdata, transform); SETMETATABLE(transform) return 1; } static int api_renderparticles_viewprojmatrix(lua_State* state) { const struct RenderParticles* render = require_self_userdata(state, "viewprojmatrix"); struct Transform3D* transform = lua_newuserdata(state, sizeof(struct Transform3D)); render->matrix_functions.viewproj_matrix(render->matrix_functions.userdata, transform); SETMETATABLE(transform) return 1; } static int api_renderparticles_inverseviewmatrix(lua_State* state) { const struct RenderParticles* render = require_self_userdata(state, "inverseviewmatrix"); struct Transform3D* transform = lua_newuserdata(state, sizeof(struct Transform3D)); render->matrix_functions.inverse_view_matrix(render->matrix_functions.userdata, transform); SETMETATABLE(transform) return 1; } static int api_renderparticles_cameraposition(lua_State* state) { const struct RenderParticles* render = require_self_userdata(state, "cameraposition"); double out[3]; render->camera_functions.position(render->camera_functions.userdata, out); lua_pushnumber(state, out[0]); lua_pushnumber(state, out[1]); lua_pushnumber(state, out[2]); return 3; } static int api_renderbillboard_vertexcount(lua_State* state) { const struct RenderBillboard* render = require_self_userdata(state, "vertexcount"); lua_pushinteger(state, render->vertex_count); return 1; } static int api_renderbillboard_verticesperimage(lua_State* state) { const struct RenderBillboard* render = require_self_userdata(state, "verticesperimage"); lua_pushinteger(state, render->vertices_per_icon); return 1; } static int api_renderbillboard_vertexpoint(lua_State* state) { const struct RenderBillboard* render = require_self_userdata(state, "vertexpoint"); const lua_Integer vertex = luaL_checkinteger(state, 2); struct Point3D* point = lua_newuserdata(state, sizeof(struct Point3D)); render->vertex_functions.xyz(vertex - 1, render->vertex_functions.userdata, point); SETMETATABLE(point) return 1; } static int api_renderbillboard_vertexeyeoffset(lua_State* state) { const struct RenderBillboard* render = require_self_userdata(state, "vertexeyeoffset"); const lua_Integer vertex = luaL_checkinteger(state, 2); double offset[2]; render->vertex_functions.eye_offset(vertex - 1, render->vertex_functions.userdata, offset); lua_pushnumber(state, offset[0]); lua_pushnumber(state, offset[1]); return 2; } static int api_renderbillboard_vertexuv(lua_State* state) { const struct RenderBillboard* render = require_self_userdata(state, "vertexuv"); const lua_Integer vertex = luaL_checkinteger(state, 2); double uv[2]; render->vertex_functions.uv(vertex - 1, render->vertex_functions.userdata, uv); lua_pushnumber(state, uv[0]); lua_pushnumber(state, uv[1]); return 2; } static int api_renderbillboard_vertexmeta(lua_State* state) { const struct RenderBillboard* render = require_self_userdata(state, "vertexmeta"); const lua_Integer index = luaL_checkinteger(state, 2); const size_t meta = render->vertex_functions.atlas_meta(index - 1, render->vertex_functions.userdata); lua_pushinteger(state, meta); return 1; } static int api_renderbillboard_atlasxywh(lua_State* state) { const struct RenderBillboard* render = require_self_userdata(state, "atlasxywh"); const lua_Integer meta = luaL_checkinteger(state, 2); int32_t xywh[4]; render->vertex_functions.atlas_xywh(meta, render->vertex_functions.userdata, xywh); lua_pushinteger(state, xywh[0]); lua_pushinteger(state, xywh[1]); lua_pushinteger(state, xywh[2]); lua_pushinteger(state, xywh[3]); return 4; } static int api_renderbillboard_textureid(lua_State* state) { const struct RenderBillboard* render = require_self_userdata(state, "textureid"); const size_t id = render->texture_functions.id(render->texture_functions.userdata); lua_pushinteger(state, id); return 1; } static int api_renderbillboard_texturesize(lua_State* state) { const struct RenderBillboard* render = require_self_userdata(state, "texturesize"); size_t size[2]; render->texture_functions.size(render->texture_functions.userdata, size); lua_pushinteger(state, size[0]); lua_pushinteger(state, size[1]); return 2; } static int api_renderbillboard_texturecompare(lua_State* state) { const struct RenderBillboard* render = require_self_userdata(state, "texturecompare"); const size_t x = luaL_checkinteger(state, 2); const size_t y = luaL_checkinteger(state, 3); size_t data_len; const void* data; get_binary_data(state, 4, &data, &data_len); const uint8_t match = render->texture_functions.compare(render->texture_functions.userdata, x, y, data_len, (const unsigned char*)data); lua_pushboolean(state, match); return 1; } static int api_renderbillboard_texturedata(lua_State* state) { const struct RenderBillboard* render = require_self_userdata(state, "texturedata"); const size_t x = luaL_checkinteger(state, 2); const size_t y = luaL_checkinteger(state, 3); const size_t len = luaL_checkinteger(state, 4); const uint8_t* ret = render->texture_functions.data(render->texture_functions.userdata, x, y); lua_pushlstring(state, (const char*)ret, len); return 1; } static int api_renderbillboard_vertexcolour(lua_State* state) { const struct RenderBillboard* render = require_self_userdata(state, "vertexcolour"); const lua_Integer index = luaL_checkinteger(state, 2); double col[4]; render->vertex_functions.colour(index - 1, render->vertex_functions.userdata, col); lua_pushnumber(state, col[0]); lua_pushnumber(state, col[1]); lua_pushnumber(state, col[2]); lua_pushnumber(state, col[3]); return 4; } static int api_renderbillboard_modelmatrix(lua_State* state) { const struct RenderBillboard* render = require_self_userdata(state, "modelmatrix"); struct Transform3D* transform = lua_newuserdata(state, sizeof(struct Transform3D)); render->matrix_functions.model_matrix(render->matrix_functions.userdata, transform); SETMETATABLE(transform) return 1; } static int api_renderbillboard_viewmatrix(lua_State* state) { const struct RenderBillboard* render = require_self_userdata(state, "viewmatrix"); struct Transform3D* transform = lua_newuserdata(state, sizeof(struct Transform3D)); render->matrix_functions.view_matrix(render->matrix_functions.userdata, transform); SETMETATABLE(transform) return 1; } static int api_renderbillboard_projmatrix(lua_State* state) { const struct RenderBillboard* render = require_self_userdata(state, "projmatrix"); struct Transform3D* transform = lua_newuserdata(state, sizeof(struct Transform3D)); render->matrix_functions.proj_matrix(render->matrix_functions.userdata, transform); SETMETATABLE(transform) return 1; } static int api_renderbillboard_viewprojmatrix(lua_State* state) { const struct RenderBillboard* render = require_self_userdata(state, "viewprojmatrix"); struct Transform3D* transform = lua_newuserdata(state, sizeof(struct Transform3D)); render->matrix_functions.viewproj_matrix(render->matrix_functions.userdata, transform); SETMETATABLE(transform) return 1; } static int api_renderbillboard_inverseviewmatrix(lua_State* state) { const struct RenderBillboard* render = require_self_userdata(state, "inverseviewmatrix"); struct Transform3D* transform = lua_newuserdata(state, sizeof(struct Transform3D)); render->matrix_functions.inverse_view_matrix(render->matrix_functions.userdata, transform); SETMETATABLE(transform) return 1; } static int api_renderbillboard_cameraposition(lua_State* state) { const struct RenderBillboard* render = require_self_userdata(state, "cameraposition"); double out[3]; render->camera_functions.position(render->camera_functions.userdata, out); lua_pushnumber(state, out[0]); lua_pushnumber(state, out[1]); lua_pushnumber(state, out[2]); return 3; } static int api_rendericon_xywh(lua_State* state) { const struct RenderIconEvent* event = require_self_userdata(state, "xywh"); lua_pushinteger(state, event->target_x); lua_pushinteger(state, event->target_y); lua_pushinteger(state, event->target_w); lua_pushinteger(state, event->target_h); return 4; } static int api_rendericon_modelcount(lua_State* state) { const struct RenderIconEvent* event = require_self_userdata(state, "modelcount"); lua_pushinteger(state, event->icon->model_count); return 1; } static int api_rendericon_modelvertexcount(lua_State* state) { const struct RenderIconEvent* event = require_self_userdata(state, "modelvertexcount"); const size_t model = luaL_checkinteger(state, 2); lua_pushinteger(state, event->icon->models[model - 1].vertex_count); return 1; } static int api_rendericon_modelvertexpoint(lua_State* state) { const struct RenderIconEvent* event = require_self_userdata(state, "modelvertexpoint"); const size_t model = luaL_checkinteger(state, 2); const size_t vertex = luaL_checkinteger(state, 3); struct Point3D* point = lua_newuserdata(state, sizeof(struct Point3D)); *point = event->icon->models[model - 1].vertices[vertex - 1].point; SETMETATABLE(point) return 1; } static int api_rendericon_modelvertexcolour(lua_State* state) { const struct RenderIconEvent* event = require_self_userdata(state, "modelvertexcolour"); const size_t model = luaL_checkinteger(state, 2); const size_t vertex = luaL_checkinteger(state, 3); for (size_t i = 0; i < 4; i += 1) { lua_pushnumber(state, event->icon->models[model - 1].vertices[vertex - 1].rgba[i]); } return 4; } static int api_rendericon_colour(lua_State* state) { const struct RenderIconEvent* event = require_self_userdata(state, "colour"); for (size_t i = 0; i < 4; i += 1) { lua_pushnumber(state, event->rgba[i]); } return 4; } static int api_rendericon_targetsize(lua_State* state) { const struct RenderIconEvent* event = require_self_userdata(state, "targetsize"); lua_pushinteger(state, event->screen_width); lua_pushinteger(state, event->screen_height); return 2; } static int api_rendericon_modelmodelmatrix(lua_State* state) { const struct RenderIconEvent* event = require_self_userdata(state, "modelmodelmatrix"); const size_t model = luaL_checkinteger(state, 2); struct Transform3D* transform = lua_newuserdata(state, sizeof(struct Transform3D)); *transform = event->icon->models[model - 1].model_matrix; SETMETATABLE(transform) return 1; } static int api_rendericon_modelviewmatrix(lua_State* state) { const struct RenderIconEvent* event = require_self_userdata(state, "modelviewmatrix"); const size_t model = luaL_checkinteger(state, 2); struct Transform3D* transform = lua_newuserdata(state, sizeof(struct Transform3D)); *transform = event->icon->models[model - 1].view_matrix; SETMETATABLE(transform) return 1; } static int api_rendericon_modelprojmatrix(lua_State* state) { const struct RenderIconEvent* event = require_self_userdata(state, "modelprojmatrix"); const size_t model = luaL_checkinteger(state, 2); struct Transform3D* transform = lua_newuserdata(state, sizeof(struct Transform3D)); *transform = event->icon->models[model - 1].projection_matrix; SETMETATABLE(transform) return 1; } static int api_rendericon_modelviewprojmatrix(lua_State* state) { const struct RenderIconEvent* event = require_self_userdata(state, "modelviewprojmatrix"); const size_t model = luaL_checkinteger(state, 2); struct Transform3D* transform = lua_newuserdata(state, sizeof(struct Transform3D)); *transform = event->icon->models[model - 1].viewproj_matrix; SETMETATABLE(transform) return 1; } static int api_repositionevent_xywh(lua_State* state) { const struct RepositionEvent* event = require_self_userdata(state, "xywh"); lua_pushinteger(state, event->x); lua_pushinteger(state, event->y); lua_pushinteger(state, event->width); lua_pushinteger(state, event->height); return 4; } static int api_repositionevent_didresize(lua_State* state) { const struct RepositionEvent* event = require_self_userdata(state, "didresize"); lua_pushboolean(state, event->did_resize); return 1; } static int api_mouseevent_xy(lua_State* state) { const struct MouseEvent* event = require_self_userdata(state, "xy"); lua_pushinteger(state, event->x); lua_pushinteger(state, event->y); return 2; } static int api_mouseevent_ctrl(lua_State* state) { const struct MouseEvent* event = require_self_userdata(state, "ctrl"); lua_pushboolean(state, event->ctrl); return 1; } static int api_mouseevent_shift(lua_State* state) { const struct MouseEvent* event = require_self_userdata(state, "shift"); lua_pushboolean(state, event->shift); return 1; } static int api_mouseevent_meta(lua_State* state) { const struct MouseEvent* event = require_self_userdata(state, "meta"); lua_pushboolean(state, event->meta); return 1; } static int api_mouseevent_alt(lua_State* state) { const struct MouseEvent* event = require_self_userdata(state, "meta"); lua_pushboolean(state, event->alt); return 1; } static int api_mouseevent_capslock(lua_State* state) { const struct MouseEvent* event = require_self_userdata(state, "capslock"); lua_pushboolean(state, event->capslock); return 1; } static int api_mouseevent_numlock(lua_State* state) { const struct MouseEvent* event = require_self_userdata(state, "numlock"); lua_pushboolean(state, event->numlock); return 1; } static int api_mouseevent_mousebuttons(lua_State* state) { const struct MouseEvent* event = require_self_userdata(state, "mousebuttons"); lua_pushboolean(state, event->mb_left); lua_pushboolean(state, event->mb_right); lua_pushboolean(state, event->mb_middle); return 3; } static int api_mousebutton_button(lua_State* state) { const struct MouseButtonEvent* event = require_self_userdata(state, "button"); lua_pushinteger(state, event->button); return 1; } static int api_scroll_direction(lua_State* state) { const struct MouseScrollEvent* event = require_self_userdata(state, "direction"); lua_pushboolean(state, event->direction); return 1; } static int api_browser_close(lua_State* state) { const struct ExternalBrowser* browser = require_self_userdata(state, "close"); if (browser->do_capture) browser->plugin->ext_browser_capture_count += 1; hashmap_delete(browser->plugin->external_browsers, &browser); const enum BoltIPCMessageTypeToHost msg_type = IPC_MSG_CLOSEBROWSER_EXTERNAL; const struct BoltIPCCloseBrowserHeader header = { .plugin_id = browser->plugin_id, .window_id = browser->id }; const BoltSocketType fd = _bolt_plugin_fd(); _bolt_ipc_send(fd, &msg_type, sizeof(msg_type)); _bolt_ipc_send(fd, &header, sizeof(header)); return 0; } static int api_browser_sendmessage(lua_State* state) { const struct ExternalBrowser* window = require_self_userdata(state, "sendmessage"); const void* str; size_t len; get_binary_data(state, 2, &str, &len); lua_getfield(state, LUA_REGISTRYINDEX, PLUGIN_REGISTRYNAME); const struct Plugin* plugin = lua_touserdata(state, -1); const uint64_t id = plugin->id; lua_pop(state, 1); const enum BoltIPCMessageTypeToHost msg_type = IPC_MSG_PLUGINMESSAGE; const struct BoltIPCPluginMessageHeader header = { .plugin_id = id, .window_id = window->id, .message_size = len }; const BoltSocketType fd = _bolt_plugin_fd(); _bolt_ipc_send(fd, &msg_type, sizeof(msg_type)); _bolt_ipc_send(fd, &header, sizeof(header)); _bolt_ipc_send(fd, str, len); return 0; } static int api_browser_enablecapture(lua_State* state) { struct ExternalBrowser* window = require_self_userdata(state, "enablecapture"); if (!window->do_capture) { window->plugin->ext_browser_capture_count += 1; window->do_capture = true; window->capture_ready = true; } return 0; } static int api_browser_disablecapture(lua_State* state) { struct ExternalBrowser* window = require_self_userdata(state, "disablecapture"); if (window->do_capture) window->plugin->ext_browser_capture_count -= 1; window->do_capture = false; return 0; } static int api_browser_showdevtools(lua_State* state) { struct ExternalBrowser* window = require_self_userdata(state, "showdevtools"); const enum BoltIPCMessageTypeToHost msg_type = IPC_MSG_SHOWDEVTOOLS_EXTERNAL; const struct BoltIPCShowDevtoolsHeader header = { .plugin_id = window->plugin_id, .window_id = window->id }; const BoltSocketType fd = _bolt_plugin_fd(); _bolt_ipc_send(fd, &msg_type, sizeof(msg_type)); _bolt_ipc_send(fd, &header, sizeof(header)); return 0; } static int api_embeddedbrowser_close(lua_State* state) { struct EmbeddedWindow* window = require_self_userdata(state, "close"); window->is_deleted = true; const enum BoltIPCMessageTypeToHost msg_type = IPC_MSG_CLOSEBROWSER_OSR; const struct BoltIPCCloseBrowserHeader header = { .plugin_id = window->plugin_id, .window_id = window->id }; const BoltSocketType fd = _bolt_plugin_fd(); _bolt_ipc_send(fd, &msg_type, sizeof(msg_type)); _bolt_ipc_send(fd, &header, sizeof(header)); return 0; } static int api_embeddedbrowser_sendmessage(lua_State* state) { const struct EmbeddedWindow* window = require_self_userdata(state, "sendmessage"); const void* str; size_t len; get_binary_data(state, 2, &str, &len); lua_getfield(state, LUA_REGISTRYINDEX, PLUGIN_REGISTRYNAME); const struct Plugin* plugin = lua_touserdata(state, -1); const uint64_t id = plugin->id; lua_pop(state, 1); const enum BoltIPCMessageTypeToHost msg_type = IPC_MSG_OSRPLUGINMESSAGE; const struct BoltIPCPluginMessageHeader header = { .plugin_id = id, .window_id = window->id, .message_size = len }; const BoltSocketType fd = _bolt_plugin_fd(); _bolt_ipc_send(fd, &msg_type, sizeof(msg_type)); _bolt_ipc_send(fd, &header, sizeof(header)); _bolt_ipc_send(fd, str, len); return 0; } static int api_embeddedbrowser_enablecapture(lua_State* state) { struct EmbeddedWindow* window = require_self_userdata(state, "enablecapture"); if (!window->do_capture) { window->do_capture = true; window->capture_ready = true; } return 0; } static int api_embeddedbrowser_disablecapture(lua_State* state) { struct EmbeddedWindow* window = require_self_userdata(state, "disablecapture"); if (window->do_capture) { window->do_capture = false; } return 0; } static int api_embeddedbrowser_showdevtools(lua_State* state) { struct EmbeddedWindow* window = require_self_userdata(state, "showdevtools"); const enum BoltIPCMessageTypeToHost msg_type = IPC_MSG_SHOWDEVTOOLS_OSR; const struct BoltIPCShowDevtoolsHeader header = { .plugin_id = window->plugin_id, .window_id = window->id }; const BoltSocketType fd = _bolt_plugin_fd(); _bolt_ipc_send(fd, &msg_type, sizeof(msg_type)); _bolt_ipc_send(fd, &header, sizeof(header)); return 0; } DEFBUFFER(float, float32, number) DEFBUFFER(double, float64, number) DEFBUFFER_1BYTE(int8_t, int8, integer) DEFBUFFER(int16_t, int16, integer) DEFBUFFER(int32_t, int32, integer) DEFBUFFER(int64_t, int64, integer) DEFBUFFER_1BYTE(uint8_t, uint8, integer) DEFBUFFER(uint16_t, uint16, integer) DEFBUFFER(uint32_t, uint32, integer) DEFBUFFER(uint64_t, uint64, integer) static int api_buffer_setstring(lua_State* state) { struct FixedBuffer* buffer = require_self_userdata(state, "setstring"); const long offset = luaL_checklong(state, 2); size_t size; const void* data = luaL_checklstring(state, 3, &size); if (offset < 0 || (offset + size) > buffer->size) { lua_pushfstring(state, "buffer setstring: out-of-bounds write (buffer length %lu, writing %lu bytes at %li)", buffer->size, size, offset); lua_error(state); } memcpy((uint8_t*)buffer->data + offset, data, size); return 0; } static int api_buffer_setbuffer(lua_State* state) { struct FixedBuffer* buffer = require_self_userdata(state, "setbuffer"); const long offset = luaL_checklong(state, 2); const struct FixedBuffer* srcbuffer = require_userdata(state, 3, "setbuffer"); if (offset < 0 || (offset + srcbuffer->size) > buffer->size) { lua_pushfstring(state, "buffer setbuffer: out-of-bounds write (buffer length %lu, writing %lu bytes at %li)", buffer->size, srcbuffer->size, offset); lua_error(state); } memcpy((uint8_t*)buffer->data + offset, srcbuffer->data, srcbuffer->size); return 0; } static int api_swapbuffers_readpixels(lua_State* state) { const struct SwapBuffersEvent* event = require_self_userdata(state, "readpixels"); const int16_t x = luaL_checkinteger(state, 2); const int16_t y = luaL_checkinteger(state, 3); const uint16_t w = luaL_checkinteger(state, 4); const uint16_t h = luaL_checkinteger(state, 5); const size_t size = w * h * 3; const struct PluginManagedFunctions* managed_functions = _bolt_plugin_managed_functions(); struct FixedBuffer* buffer = lua_newuserdata(state, sizeof(struct FixedBuffer)); buffer->data = malloc(size); if (!buffer->data) { lua_pushfstring(state, "readpixels: heap error, failed to allocate %i bytes", size); lua_error(state); } buffer->size = size; SETMETATABLE(buffer) managed_functions->read_screen_pixels(x, y, w, h, buffer->data); return 1; } static int api_swapbuffers_copytosurface(lua_State* state) { const struct SwapBuffersEvent* event = require_self_userdata(state, "copytosurface"); const struct SurfaceFunctions* target = require_userdata(state, 2, "copytosurface"); const int sx = luaL_checkinteger(state, 3); const int sy = luaL_checkinteger(state, 4); const int sw = luaL_checkinteger(state, 5); const int sh = luaL_checkinteger(state, 6); const int dx = luaL_checkinteger(state, 7); const int dy = luaL_checkinteger(state, 8); const int dw = luaL_checkinteger(state, 9); const int dh = luaL_checkinteger(state, 10); const struct PluginManagedFunctions* managed_functions = _bolt_plugin_managed_functions(); managed_functions->copy_screen(target->userdata, sx, sy, sw, sh, dx, dy, dw, dh); return 0; } static int api_shaderprogram_setattribute(lua_State* state) { struct ShaderProgramFunctions* program = require_self_userdata(state, "setattribute"); const lua_Integer attribute = luaL_checkinteger(state, 2); if (attribute < 0 || attribute >= MAX_PROGRAM_BINDINGS) { lua_pushfstring(state, "setattribute: invalid attribute %li - must be positive and less than %i", attribute, MAX_PROGRAM_BINDINGS); lua_error(state); } const uint8_t width = luaL_checkinteger(state, 3); const uint8_t is_signed = lua_toboolean(state, 4); const uint8_t is_float = lua_toboolean(state, 5); const uint8_t size = luaL_checkinteger(state, 6); const lua_Integer offset = luaL_checkinteger(state, 7); const lua_Integer stride = luaL_checkinteger(state, 8); if (!program->set_attribute(program->userdata, (uint8_t)attribute, width, is_signed, is_float, size, (uint32_t)offset, (uint32_t)stride)) { lua_pushfstring(state, "setattribute: invalid arguments or attribute is already enabled", attribute, MAX_PROGRAM_BINDINGS); lua_error(state); } return 0; } #define DEFSETUNIFORM(N) \ static int api_shaderprogram_setuniform##N##i(lua_State* state) { \ struct ShaderProgramFunctions* program = require_self_userdata(state, "setuniform" #N "i"); \ const lua_Integer location = luaL_checkinteger(state, 2); \ int values[N]; \ for (size_t i = 0; i < N; i += 1) { \ values[i] = (int)luaL_checkinteger(state, i + 3); \ } \ program->set_uniform_ints(program->userdata, location, N, values); \ return 0; \ } \ static int api_shaderprogram_setuniform##N##f(lua_State* state) { \ struct ShaderProgramFunctions* program = require_self_userdata(state, "setuniform" #N "f"); \ const lua_Integer location = luaL_checkinteger(state, 2); \ double values[N]; \ for (size_t i = 0; i < N; i += 1) { \ values[i] = (double)luaL_checknumber(state, i + 3); \ } \ program->set_uniform_floats(program->userdata, location, N, values); \ return 0; \ } DEFSETUNIFORM(1) DEFSETUNIFORM(2) DEFSETUNIFORM(3) DEFSETUNIFORM(4) #undef DEFSETUNIFORM #define DEFSETUNIFORMMATRIX(N) \ static int api_shaderprogram_setuniformmatrix##N##f(lua_State* state) { \ struct ShaderProgramFunctions* program = require_self_userdata(state, "setuniformmatrix" #N "f"); \ const lua_Integer location = luaL_checkinteger(state, 2); \ const uint8_t transpose = lua_toboolean(state, 3); \ double values[N * N]; \ for (size_t i = 0; i < (N * N); i += 1) { \ values[i] = (double)luaL_checknumber(state, i + 4); \ } \ program->set_uniform_matrix(program->userdata, location, transpose, N, values); \ return 0; \ } DEFSETUNIFORMMATRIX(2) DEFSETUNIFORMMATRIX(3) DEFSETUNIFORMMATRIX(4) #undef DEFSETUNIFORMMATRIX static int api_shaderprogram_setuniformsurface(lua_State* state) { struct ShaderProgramFunctions* program = require_self_userdata(state, "setuniformsurface"); const lua_Integer location = luaL_checkinteger(state, 2); const struct SurfaceFunctions* surface = require_userdata(state, 3, "setuniformsurface"); program->set_uniform_surface(program->userdata, location, surface->userdata); return 0; } static int api_shaderprogram_setuniformdepthbuffer(lua_State* state) { struct ShaderProgramFunctions* program = require_self_userdata(state, "setuniformdepthbuffer"); const lua_Integer location = luaL_checkinteger(state, 2); const struct RenderGameViewEvent* event = require_userdata(state, 3, "setuniformdepthbuffer"); program->set_uniform_depthbuffer(program->userdata, event->functions.userdata, location); return 0; } static int api_shaderprogram_drawtosurface(lua_State* state) { const struct ShaderProgramFunctions* program = require_self_userdata(state, "drawtosurface"); const struct SurfaceFunctions* surface = require_userdata(state, 2, "drawtosurface"); const struct ShaderBufferFunctions* buffer = require_userdata(state, 3, "drawtosurface"); const lua_Integer vertex_count = luaL_checkinteger(state, 4); if (vertex_count > UINT32_MAX) { lua_pushfstring(state, "drawtosurface: too many vertices: %lu, maximum is %u", vertex_count, UINT32_MAX); lua_error(state); } program->draw_to_surface(program->userdata, surface->userdata, buffer->userdata, vertex_count); return 0; } static int api_shaderprogram_drawtogameview(lua_State* state) { const struct ShaderProgramFunctions* program = require_self_userdata(state, "drawtogameview"); const struct RenderGameViewEvent* gameview = require_userdata(state, 2, "drawtogameview"); const struct ShaderBufferFunctions* buffer = require_userdata(state, 3, "drawtogameview"); const lua_Integer vertex_count = luaL_checkinteger(state, 4); if (vertex_count > UINT32_MAX) { lua_pushfstring(state, "drawtogameview: too many vertices: %lu, maximum is %u", vertex_count, UINT32_MAX); lua_error(state); } program->draw_to_gameview(program->userdata, gameview->functions.userdata, buffer->userdata, vertex_count); return 0; } /* below here is API function-list builders, declared in plugin_api.h */ struct ApiFuncTemplate { int (*func)(lua_State*); const char* name; size_t size; }; static void populate_table(lua_State* state, const struct ApiFuncTemplate* functions, size_t count) { for (size_t i = 0; i < count; i += 1) { const struct ApiFuncTemplate* function = &functions[i]; lua_pushlstring(state, function->name, function->size); lua_pushcfunction(state, function->func); lua_settable(state, -3); } } static void push_metatable(lua_State* state, const struct ApiFuncTemplate* functions, size_t count) { lua_newtable(state); lua_pushliteral(state, "__index"); lua_createtable(state, 0, count); populate_table(state, functions, count); lua_settable(state, -3); } static void push_metatable_with_gc(lua_State* state, const struct ApiFuncTemplate* functions, size_t count, int(*gc)(lua_State*)) { push_metatable(state, functions, count); lua_pushliteral(state, "__gc"); lua_pushcfunction(state, gc); lua_settable(state, -3); } #define BOLTFUNC(NAME) {.func=api_##NAME, .name=#NAME, .size=sizeof #NAME - sizeof *#NAME} static struct ApiFuncTemplate bolt_functions[] = { BOLTFUNC(apiversion), BOLTFUNC(checkversion), BOLTFUNC(close), BOLTFUNC(time), BOLTFUNC(datetime), BOLTFUNC(weekday), BOLTFUNC(playerposition), BOLTFUNC(gamewindowsize), BOLTFUNC(gameviewxywh), BOLTFUNC(flashwindow), BOLTFUNC(isfocused), BOLTFUNC(characterid), BOLTFUNC(loadfile), BOLTFUNC(loadconfig), BOLTFUNC(saveconfig), BOLTFUNC(onrender2d), BOLTFUNC(onrender3d), BOLTFUNC(onrenderparticles), BOLTFUNC(onrenderbillboard), BOLTFUNC(onrendericon), BOLTFUNC(onrenderbigicon), BOLTFUNC(onminimapterrain), BOLTFUNC(onminimaprender2d), BOLTFUNC(onrenderminimap), BOLTFUNC(onrendergameview), BOLTFUNC(onswapbuffers), BOLTFUNC(onmousemotion), BOLTFUNC(onmousebutton), BOLTFUNC(onmousebuttonup), BOLTFUNC(onscroll), BOLTFUNC(createsurface), BOLTFUNC(createsurfacefromrgba), BOLTFUNC(createsurfacefrompng), BOLTFUNC(createwindow), BOLTFUNC(createbrowser), BOLTFUNC(createembeddedbrowser), BOLTFUNC(point), BOLTFUNC(createbuffer), BOLTFUNC(createvertexshader), BOLTFUNC(createfragmentshader), BOLTFUNC(createshaderprogram), BOLTFUNC(createshaderbuffer), BOLTFUNC(buffergetint8), BOLTFUNC(buffergetint16), BOLTFUNC(buffergetint32), BOLTFUNC(buffergetint64), BOLTFUNC(buffergetuint8), BOLTFUNC(buffergetuint16), BOLTFUNC(buffergetuint32), BOLTFUNC(buffergetuint64), BOLTFUNC(buffergetfloat32), BOLTFUNC(buffergetfloat64), }; #undef BOLTFUNC #define BOLTFUNC(NAME, OBJECT) {.func=api_##OBJECT##_##NAME, .name=#NAME, .size=sizeof(#NAME)-sizeof(*#NAME)} #define BOLTALIAS(REALNAME, ALIAS, OBJECT) {.func=api_##OBJECT##_##REALNAME, .name=#ALIAS, .size=sizeof(#ALIAS)-sizeof(*#ALIAS)} static struct ApiFuncTemplate render2d_functions[] = { BOLTFUNC(vertexcount, batch2d), BOLTFUNC(verticesperimage, batch2d), BOLTFUNC(targetsize, batch2d), BOLTFUNC(vertexxy, batch2d), BOLTFUNC(vertexatlasdetails, batch2d), BOLTFUNC(vertexuv, batch2d), BOLTFUNC(vertexcolour, batch2d), BOLTFUNC(textureid, batch2d), BOLTFUNC(texturesize, batch2d), BOLTFUNC(texturecompare, batch2d), BOLTFUNC(texturedata, batch2d), BOLTALIAS(vertexcolour, vertexcolor, batch2d), }; static struct ApiFuncTemplate render3d_functions[] = { BOLTFUNC(vertexcount, render3d), BOLTFUNC(vertexpoint, render3d), BOLTFUNC(modelmatrix, render3d), BOLTFUNC(viewmatrix, render3d), BOLTFUNC(projmatrix, render3d), BOLTFUNC(viewprojmatrix, render3d), BOLTFUNC(inverseviewmatrix, render3d), BOLTFUNC(vertexmeta, render3d), BOLTFUNC(atlasxywh, render3d), BOLTFUNC(vertexuv, render3d), BOLTFUNC(vertexcolour, render3d), BOLTFUNC(textureid, render3d), BOLTFUNC(texturesize, render3d), BOLTFUNC(texturecompare, render3d), BOLTFUNC(texturedata, render3d), BOLTFUNC(vertexanimation, render3d), BOLTFUNC(animated, render3d), BOLTFUNC(cameraposition, render3d), BOLTALIAS(vertexcolour, vertexcolor, render3d), BOLTALIAS(projmatrix, projectionmatrix, render3d), }; static struct ApiFuncTemplate renderparticles_functions[] = { BOLTFUNC(vertexcount, renderparticles), BOLTFUNC(vertexparticleorigin, renderparticles), BOLTFUNC(vertexworldoffset, renderparticles), BOLTFUNC(vertexeyeoffset, renderparticles), BOLTFUNC(vertexuv, renderparticles), BOLTFUNC(vertexcolour, renderparticles), BOLTFUNC(vertexmeta, renderparticles), BOLTFUNC(atlasxywh, renderparticles), BOLTFUNC(textureid, renderparticles), BOLTFUNC(texturesize, renderparticles), BOLTFUNC(texturecompare, renderparticles), BOLTFUNC(texturedata, renderparticles), BOLTFUNC(viewmatrix, renderparticles), BOLTFUNC(projmatrix, renderparticles), BOLTFUNC(viewprojmatrix, renderparticles), BOLTFUNC(inverseviewmatrix, renderparticles), BOLTFUNC(cameraposition, renderparticles), BOLTALIAS(vertexcolour, vertexcolor, renderparticles), BOLTALIAS(projmatrix, projectionmatrix, renderparticles), }; static struct ApiFuncTemplate renderbillboard_functions[] = { BOLTFUNC(vertexcount, renderbillboard), BOLTFUNC(verticesperimage, renderbillboard), BOLTFUNC(vertexpoint, renderbillboard), BOLTFUNC(vertexeyeoffset, renderbillboard), BOLTFUNC(vertexuv, renderbillboard), BOLTFUNC(vertexcolour, renderbillboard), BOLTFUNC(vertexmeta, renderbillboard), BOLTFUNC(atlasxywh, renderbillboard), BOLTFUNC(textureid, renderbillboard), BOLTFUNC(texturesize, renderbillboard), BOLTFUNC(texturecompare, renderbillboard), BOLTFUNC(texturedata, renderbillboard), BOLTFUNC(modelmatrix, renderbillboard), BOLTFUNC(viewmatrix, renderbillboard), BOLTFUNC(projmatrix, renderbillboard), BOLTFUNC(viewprojmatrix, renderbillboard), BOLTFUNC(inverseviewmatrix, renderbillboard), BOLTFUNC(cameraposition, renderbillboard), BOLTALIAS(vertexcolour, vertexcolor, renderbillboard), BOLTALIAS(projmatrix, projectionmatrix, renderbillboard), }; static struct ApiFuncTemplate rendericon_functions[] = { BOLTFUNC(xywh, rendericon), BOLTFUNC(modelcount, rendericon), BOLTFUNC(modelvertexcount, rendericon), BOLTFUNC(modelvertexpoint, rendericon), BOLTFUNC(modelvertexcolour, rendericon), BOLTFUNC(modelmodelmatrix, rendericon), BOLTFUNC(modelviewmatrix, rendericon), BOLTFUNC(modelprojmatrix, rendericon), BOLTALIAS(modelprojmatrix, modelprojectionmatrix, rendericon), BOLTFUNC(modelviewprojmatrix, rendericon), BOLTALIAS(modelvertexcolour, modelvertexcolor, rendericon), BOLTFUNC(colour, rendericon), BOLTALIAS(colour, color, rendericon), BOLTFUNC(targetsize, rendericon), }; static struct ApiFuncTemplate minimapterrain_functions[] = { BOLTFUNC(angle, minimapterrain), BOLTFUNC(scale, minimapterrain), BOLTFUNC(position, minimapterrain), }; static struct ApiFuncTemplate renderminimap_functions[] = { BOLTFUNC(sourcexywh, renderminimap), BOLTFUNC(targetxywh, renderminimap), BOLTFUNC(targetsize, renderminimap), }; static struct ApiFuncTemplate rendergameview_functions[] = { BOLTFUNC(size, rendergameview), }; static struct ApiFuncTemplate point_functions[] = { BOLTFUNC(transform, point), BOLTFUNC(get, point), BOLTFUNC(toscreen, point), BOLTALIAS(toscreen, aspixels, point), BOLTFUNC(togameview, point), }; static struct ApiFuncTemplate transform_functions[] = { BOLTFUNC(decompose, transform), BOLTFUNC(get, transform), }; static struct ApiFuncTemplate buffer_functions[] = { BOLTFUNC(getint8, buffer), BOLTFUNC(getint16, buffer), BOLTFUNC(getint32, buffer), BOLTFUNC(getint64, buffer), BOLTFUNC(getuint8, buffer), BOLTFUNC(getuint16, buffer), BOLTFUNC(getuint32, buffer), BOLTFUNC(getuint64, buffer), BOLTFUNC(getfloat32, buffer), BOLTFUNC(getfloat64, buffer), BOLTFUNC(setint8, buffer), BOLTFUNC(setint16, buffer), BOLTFUNC(setint32, buffer), BOLTFUNC(setint64, buffer), BOLTFUNC(setuint8, buffer), BOLTFUNC(setuint16, buffer), BOLTFUNC(setuint32, buffer), BOLTFUNC(setuint64, buffer), BOLTFUNC(setfloat32, buffer), BOLTFUNC(setfloat64, buffer), BOLTFUNC(setstring, buffer), BOLTFUNC(setbuffer, buffer), }; static struct ApiFuncTemplate swapbuffers_functions[] = { BOLTFUNC(readpixels, swapbuffers), BOLTFUNC(copytosurface, swapbuffers), }; static struct ApiFuncTemplate shaderprogram_functions[] = { BOLTFUNC(setattribute, shaderprogram), BOLTFUNC(setuniform1i, shaderprogram), BOLTFUNC(setuniform2i, shaderprogram), BOLTFUNC(setuniform3i, shaderprogram), BOLTFUNC(setuniform4i, shaderprogram), BOLTFUNC(setuniform1f, shaderprogram), BOLTFUNC(setuniform2f, shaderprogram), BOLTFUNC(setuniform3f, shaderprogram), BOLTFUNC(setuniform4f, shaderprogram), BOLTFUNC(setuniformmatrix2f, shaderprogram), BOLTFUNC(setuniformmatrix3f, shaderprogram), BOLTFUNC(setuniformmatrix4f, shaderprogram), BOLTFUNC(setuniformsurface, shaderprogram), BOLTFUNC(setuniformdepthbuffer, shaderprogram), BOLTFUNC(drawtosurface, shaderprogram), BOLTFUNC(drawtogameview, shaderprogram), }; static struct ApiFuncTemplate surface_functions[] = { BOLTFUNC(clear, surface), BOLTFUNC(subimage, surface), BOLTFUNC(drawtoscreen, surface), BOLTFUNC(drawtosurface, surface), BOLTFUNC(drawtogameview, surface), BOLTFUNC(drawtowindow, surface), BOLTFUNC(settint, surface), BOLTFUNC(setalpha, surface), }; static struct ApiFuncTemplate window_functions[] = { BOLTFUNC(close, window), BOLTFUNC(id, window), BOLTFUNC(clear, window), BOLTFUNC(subimage, window), BOLTFUNC(startreposition, window), BOLTFUNC(cancelreposition, window), BOLTFUNC(xywh, window), BOLTFUNC(onreposition, window), BOLTFUNC(onmousemotion, window), BOLTFUNC(onmousebutton, window), BOLTFUNC(onmousebuttonup, window), BOLTFUNC(onscroll, window), BOLTFUNC(onmouseleave, window), }; static struct ApiFuncTemplate browser_functions[] = { BOLTFUNC(close, browser), BOLTFUNC(sendmessage, browser), BOLTFUNC(enablecapture, browser), BOLTFUNC(disablecapture, browser), BOLTFUNC(showdevtools, browser), BOLTFUNC(oncloserequest, browser), BOLTFUNC(onmessage, browser), }; static struct ApiFuncTemplate embeddedbrowser_functions[] = { BOLTFUNC(close, embeddedbrowser), BOLTFUNC(sendmessage, embeddedbrowser), BOLTFUNC(startreposition, window), BOLTFUNC(cancelreposition, window), BOLTFUNC(xywh, window), BOLTFUNC(enablecapture, embeddedbrowser), BOLTFUNC(disablecapture, embeddedbrowser), BOLTFUNC(showdevtools, embeddedbrowser), BOLTFUNC(oncloserequest, browser), BOLTFUNC(onmessage, browser), BOLTFUNC(onreposition, browser), BOLTFUNC(onmousemotion, browser), BOLTFUNC(onmousebutton, browser), BOLTFUNC(onmousebuttonup, browser), BOLTFUNC(onscroll, browser), BOLTFUNC(onmouseleave, browser), }; static struct ApiFuncTemplate reposition_functions[] = { BOLTFUNC(xywh, repositionevent), BOLTFUNC(didresize, repositionevent) }; static struct ApiFuncTemplate mousemotion_functions[] = { BOLTFUNC(xy, mouseevent), BOLTFUNC(ctrl, mouseevent), BOLTFUNC(shift, mouseevent), BOLTFUNC(meta, mouseevent), BOLTFUNC(alt, mouseevent), BOLTFUNC(capslock, mouseevent), BOLTFUNC(numlock, mouseevent), BOLTFUNC(mousebuttons, mouseevent), }; static struct ApiFuncTemplate mousebutton_functions[] = { BOLTFUNC(xy, mouseevent), BOLTFUNC(ctrl, mouseevent), BOLTFUNC(shift, mouseevent), BOLTFUNC(meta, mouseevent), BOLTFUNC(alt, mouseevent), BOLTFUNC(capslock, mouseevent), BOLTFUNC(numlock, mouseevent), BOLTFUNC(mousebuttons, mouseevent), BOLTFUNC(button, mousebutton), }; static struct ApiFuncTemplate scroll_functions[] = { BOLTFUNC(xy, mouseevent), BOLTFUNC(ctrl, mouseevent), BOLTFUNC(shift, mouseevent), BOLTFUNC(meta, mouseevent), BOLTFUNC(alt, mouseevent), BOLTFUNC(capslock, mouseevent), BOLTFUNC(numlock, mouseevent), BOLTFUNC(mousebuttons, mouseevent), BOLTFUNC(direction, scroll), }; static struct ApiFuncTemplate mouseleave_functions[] = { BOLTFUNC(xy, mouseevent), BOLTFUNC(ctrl, mouseevent), BOLTFUNC(shift, mouseevent), BOLTFUNC(meta, mouseevent), BOLTFUNC(alt, mouseevent), BOLTFUNC(capslock, mouseevent), BOLTFUNC(numlock, mouseevent), BOLTFUNC(mousebuttons, mouseevent), }; #undef BOLTFUNC #undef BOLTALIAS void _bolt_api_push_bolt_table(lua_State* state) { const size_t count = sizeof bolt_functions / sizeof *bolt_functions; lua_createtable(state, 0, count); populate_table(state, bolt_functions, count); } #define DEFPUSHMETA(NAME) \ void _bolt_api_push_metatable_##NAME(lua_State* state) { \ push_metatable(state, NAME##_functions, sizeof NAME##_functions / sizeof *NAME##_functions); \ } #define DEFPUSHMETAGC(NAME) \ void _bolt_api_push_metatable_##NAME(lua_State* state) { \ push_metatable_with_gc(state, NAME##_functions, sizeof NAME##_functions / sizeof *NAME##_functions, NAME##_gc); \ } #define DEFPUSHEMPTYMETA(NAME) \ void _bolt_api_push_metatable_##NAME(lua_State* state) { \ push_metatable(state, NULL, 0); \ } #define DEFPUSHEMPTYMETAGC(NAME) \ void _bolt_api_push_metatable_##NAME(lua_State* state) { \ push_metatable_with_gc(state, NULL, 0, NAME##_gc); \ } DEFPUSHMETA(render2d) DEFPUSHMETA(render3d) DEFPUSHMETA(renderparticles) DEFPUSHMETA(renderbillboard) DEFPUSHMETA(rendericon) DEFPUSHMETA(minimapterrain) DEFPUSHMETA(renderminimap) DEFPUSHMETA(rendergameview) DEFPUSHMETA(point) DEFPUSHMETA(transform) DEFPUSHMETAGC(buffer) DEFPUSHMETA(swapbuffers) DEFPUSHMETAGC(surface) DEFPUSHEMPTYMETAGC(shader) DEFPUSHMETAGC(shaderprogram) DEFPUSHEMPTYMETAGC(shaderbuffer) DEFPUSHMETA(window) DEFPUSHMETA(browser) DEFPUSHMETA(embeddedbrowser) DEFPUSHMETA(reposition) DEFPUSHMETA(mousemotion) DEFPUSHMETA(mousebutton) DEFPUSHMETA(scroll) DEFPUSHMETA(mouseleave)