Files
Bolt/src/library/plugin/plugin_api.c
2025-02-11 20:32:57 +00:00

2308 lines
88 KiB
C

#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 <lauxlib.h>
#include <math.h>
#include <string.h>
#include <time.h>
#if defined(_WIN32)
#define getpid _getpid
#else
#include <unistd.h>
#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(rendericon)
DEFINE_CALLBACK(renderbigicon)
DEFINE_CALLBACK(minimapterrain)
DEFINE_CALLBACK(minimaprender2d)
DEFINE_CALLBACK(renderminimap)
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(&micros)) {
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) {
lua_pushfstring(state, "createsurfacefrompng: error opening file '%s'", (char*)full_path);
lua_error(state);
}
fseek(f, 0, SEEK_END);
const long png_size = ftell(f);
fseek(f, 0, SEEK_SET);
void* png = lua_newuserdata(state, png_size);
if (fread(png, 1, png_size, f) < png_size) {
fclose(f);
lua_pushfstring(state, "createsurfacefrompng: error reading file '%s'", (char*)full_path);
lua_error(state);
}
fclose(f);
#define CALL_SPNG(FUNC, ...) err = FUNC(__VA_ARGS__); if(err){free(rgba);lua_pushfstring(state,"createsurfacefrompng: error decoding file '%s': " #FUNC " returned %i",(char*)full_path,err);lua_error(state);}
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);
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;
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_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_aspixels(lua_State* state) {
const struct Point3D* point = require_self_userdata(state, "aspixels");
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;
}
#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_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_projectionmatrix(lua_State* state) {
const struct Render3D* render = require_self_userdata(state, "projectionmatrix");
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_vertexmeta(lua_State* state) {
const struct Render3D* render = require_self_userdata(state, "vertexmeta");
const lua_Integer index = luaL_checkinteger(state, 2);
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_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);
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_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_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_modelprojectionmatrix(lua_State* state) {
const struct RenderIconEvent* event = require_self_userdata(state, "modelprojectionmatrix");
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_pushinteger(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_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_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;
}
/* 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(onrendericon),
BOLTFUNC(onrenderbigicon),
BOLTFUNC(onminimapterrain),
BOLTFUNC(onminimaprender2d),
BOLTFUNC(onrenderminimap),
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(projectionmatrix, render3d),
BOLTFUNC(viewprojmatrix, 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),
BOLTALIAS(vertexcolour, vertexcolor, 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(inverseviewmatrix, renderparticles),
BOLTALIAS(vertexcolour, vertexcolor, renderparticles),
};
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(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 point_functions[] = {
BOLTFUNC(transform, point),
BOLTFUNC(get, point),
BOLTFUNC(aspixels, 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 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(drawtosurface, shaderprogram),
};
static struct ApiFuncTemplate surface_functions[] = {
BOLTFUNC(clear, surface),
BOLTFUNC(subimage, surface),
BOLTFUNC(drawtoscreen, surface),
BOLTFUNC(drawtosurface, 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(rendericon)
DEFPUSHMETA(minimapterrain)
DEFPUSHMETA(renderminimap)
DEFPUSHMETA(point)
DEFPUSHMETA(transform)
DEFPUSHMETAGC(buffer)
DEFPUSHEMPTYMETA(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)