669 lines
19 KiB
C
669 lines
19 KiB
C
#include "ui_lua.h"
|
|
#include "gpu.h"
|
|
#include "hsv.h"
|
|
|
|
#include "GLFW/glfw3.h"
|
|
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
|
|
#define UI_LUA_CONTAINER "ui_container"
|
|
#define UI_LUA_CONTEXT "ui_context"
|
|
#define UI_LUA_GPU "ui_gpu"
|
|
|
|
#define UI_LUA_ELEMENT_META "ui_element"
|
|
|
|
// Opaque element handle passed to scripts. container_id + generation let the
|
|
// binding detect stale handles (container unloaded / slot recycled) and raise
|
|
// a Lua error instead of touching a reused slot.
|
|
typedef struct UIElementHandleStruct {
|
|
uint32_t slot;
|
|
uint32_t generation;
|
|
uint32_t is_string;
|
|
uint32_t container_id;
|
|
} UIElementHandle;
|
|
|
|
static void* registry_pointer(lua_State* L, const char* key) {
|
|
lua_getfield(L, LUA_REGISTRYINDEX, key);
|
|
void* ptr = lua_touserdata(L, -1);
|
|
lua_pop(L, 1);
|
|
return ptr;
|
|
}
|
|
|
|
static void set_registry_pointers(lua_State* L, Container* c, UIContext* ui, RenderContext* gpu) {
|
|
lua_pushlightuserdata(L, c);
|
|
lua_setfield(L, LUA_REGISTRYINDEX, UI_LUA_CONTAINER);
|
|
lua_pushlightuserdata(L, ui);
|
|
lua_setfield(L, LUA_REGISTRYINDEX, UI_LUA_CONTEXT);
|
|
lua_pushlightuserdata(L, gpu);
|
|
lua_setfield(L, LUA_REGISTRYINDEX, UI_LUA_GPU);
|
|
}
|
|
|
|
// ---------- handle plumbing ----------
|
|
|
|
static UIElementHandle* check_handle(lua_State* L, int idx) {
|
|
return luaL_checkudata(L, idx, UI_LUA_ELEMENT_META);
|
|
}
|
|
|
|
static Container* resolve_handle(lua_State* L, UIElementHandle* h) {
|
|
UIContext* ui = registry_pointer(L, UI_LUA_CONTEXT);
|
|
Container* c = context_container(h->container_id, ui);
|
|
if(c == NULL) {
|
|
luaL_error(L, "element handle's container %d is unloaded", h->container_id);
|
|
}
|
|
if(h->is_string) {
|
|
if(h->slot >= c->num_strings
|
|
|| c->string_live[h->slot] == 0
|
|
|| c->string_generation[h->slot] != h->generation) {
|
|
luaL_error(L, "stale string handle (slot %d)", h->slot);
|
|
}
|
|
} else {
|
|
if(h->slot >= c->num_drawables
|
|
|| c->drawable_live[h->slot] == 0
|
|
|| c->drawable_generation[h->slot] != h->generation) {
|
|
luaL_error(L, "stale element handle (slot %d)", h->slot);
|
|
}
|
|
}
|
|
return c;
|
|
}
|
|
|
|
// Pushes the handle userdata and refs it against the slot so dispatch can
|
|
// pass the same object back to the script
|
|
static void push_new_handle(lua_State* L, Container* c, uint32_t slot, uint32_t is_string) {
|
|
UIElementHandle* h = lua_newuserdata(L, sizeof(UIElementHandle));
|
|
h->slot = slot;
|
|
h->is_string = is_string;
|
|
h->container_id = c->id;
|
|
h->generation = is_string ? c->string_generation[slot] : c->drawable_generation[slot];
|
|
luaL_setmetatable(L, UI_LUA_ELEMENT_META);
|
|
|
|
lua_pushvalue(L, -1);
|
|
int ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
|
if(is_string) {
|
|
c->string_ref[slot] = ref;
|
|
} else {
|
|
c->drawable_ref[slot] = ref;
|
|
}
|
|
}
|
|
|
|
static void push_element_handle(lua_State* L, Container* c, uint32_t element) {
|
|
int ref = (element < c->cap_drawables) ? c->drawable_ref[element] : LUA_NOREF;
|
|
if(ref == LUA_NOREF) {
|
|
lua_pushnil(L);
|
|
} else {
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
|
|
}
|
|
}
|
|
|
|
// ---------- declaration table parsing ----------
|
|
|
|
static float opt_number_field(lua_State* L, int table, const char* key, float def) {
|
|
lua_getfield(L, table, key);
|
|
float v = lua_isnil(L, -1) ? def : (float)luaL_checknumber(L, -1);
|
|
lua_pop(L, 1);
|
|
return v;
|
|
}
|
|
|
|
static uint32_t opt_integer_field(lua_State* L, int table, const char* key, uint32_t def) {
|
|
lua_getfield(L, table, key);
|
|
uint32_t v = lua_isnil(L, -1) ? def : (uint32_t)luaL_checkinteger(L, -1);
|
|
lua_pop(L, 1);
|
|
return v;
|
|
}
|
|
|
|
static void opt_vec2_field(lua_State* L, int table, const char* key, vec2 out) {
|
|
out[0] = 0;
|
|
out[1] = 0;
|
|
lua_getfield(L, table, key);
|
|
if(!lua_isnil(L, -1)) {
|
|
luaL_checktype(L, -1, LUA_TTABLE);
|
|
for(int i = 0; i < 2; i++) {
|
|
lua_rawgeti(L, -1, i + 1);
|
|
out[i] = (float)luaL_checknumber(L, -1);
|
|
lua_pop(L, 1);
|
|
}
|
|
}
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
// Reads {r, g, b, a} from the table at the top of the stack
|
|
static void read_vec4(lua_State* L, vec4 out) {
|
|
luaL_checktype(L, -1, LUA_TTABLE);
|
|
for(int i = 0; i < 4; i++) {
|
|
lua_rawgeti(L, -1, i + 1);
|
|
out[i] = (float)luaL_checknumber(L, -1);
|
|
lua_pop(L, 1);
|
|
}
|
|
}
|
|
|
|
// color={r,g,b,a} fills all corners; colors={{...},{...},{...},{...}} sets
|
|
// each corner
|
|
static void opt_corner_colors(lua_State* L, int table, vec4 corners[4]) {
|
|
memset(corners, 0, sizeof(vec4)*4);
|
|
|
|
lua_getfield(L, table, "color");
|
|
if(!lua_isnil(L, -1)) {
|
|
vec4 color;
|
|
read_vec4(L, color);
|
|
for(int i = 0; i < 4; i++) {
|
|
memcpy(corners[i], color, sizeof(vec4));
|
|
}
|
|
lua_pop(L, 1);
|
|
return;
|
|
}
|
|
lua_pop(L, 1);
|
|
|
|
lua_getfield(L, table, "colors");
|
|
if(!lua_isnil(L, -1)) {
|
|
luaL_checktype(L, -1, LUA_TTABLE);
|
|
for(int i = 0; i < 4; i++) {
|
|
lua_rawgeti(L, -1, i + 1);
|
|
read_vec4(L, corners[i]);
|
|
lua_pop(L, 1);
|
|
}
|
|
}
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
// ---------- element constructors ----------
|
|
|
|
// ui.rect{pos=, size=, z=, type=, var=, events=, color=|colors=} -> handle
|
|
static int lua_ui_rect(lua_State* L) {
|
|
Container* container = registry_pointer(L, UI_LUA_CONTAINER);
|
|
RenderContext* gpu = registry_pointer(L, UI_LUA_GPU);
|
|
luaL_checktype(L, 1, LUA_TTABLE);
|
|
|
|
GPUDrawable drawable;
|
|
memset(&drawable, 0, sizeof(GPUDrawable));
|
|
opt_vec2_field(L, 1, "pos", drawable.pos);
|
|
opt_vec2_field(L, 1, "size", drawable.size);
|
|
opt_corner_colors(L, 1, drawable.color);
|
|
drawable.type = opt_integer_field(L, 1, "type", DRAWABLE_TYPE_RECT);
|
|
drawable.var = opt_integer_field(L, 1, "var", 0);
|
|
drawable.events = opt_integer_field(L, 1, "events", 0);
|
|
drawable.z = opt_number_field(L, 1, "z", 0);
|
|
|
|
uint32_t slot;
|
|
if(ui_create_drawable(container, &drawable, gpu, &slot) != VK_SUCCESS) {
|
|
return luaL_error(L, "ui.rect: failed to create drawable");
|
|
}
|
|
|
|
push_new_handle(L, container, slot, 0);
|
|
return 1;
|
|
}
|
|
|
|
// ui.text{pos=, z=, size=, color=, font=, max_length=, text=} -> handle
|
|
static int lua_ui_text(lua_State* L) {
|
|
Container* container = registry_pointer(L, UI_LUA_CONTAINER);
|
|
UIContext* ui = registry_pointer(L, UI_LUA_CONTEXT);
|
|
RenderContext* gpu = registry_pointer(L, UI_LUA_GPU);
|
|
luaL_checktype(L, 1, LUA_TTABLE);
|
|
|
|
GPUString string;
|
|
memset(&string, 0, sizeof(GPUString));
|
|
opt_vec2_field(L, 1, "pos", string.pos);
|
|
string.size = opt_number_field(L, 1, "size", 16);
|
|
string.font = opt_integer_field(L, 1, "font", 0);
|
|
string.max_length = opt_integer_field(L, 1, "max_length", 0);
|
|
string.z = opt_number_field(L, 1, "z", 0);
|
|
|
|
string.color[0] = 1;
|
|
string.color[1] = 1;
|
|
string.color[2] = 1;
|
|
string.color[3] = 1;
|
|
lua_getfield(L, 1, "color");
|
|
if(!lua_isnil(L, -1)) {
|
|
read_vec4(L, string.color);
|
|
}
|
|
lua_pop(L, 1);
|
|
|
|
uint32_t slot;
|
|
if(ui_create_string(container, &string, gpu, &slot) != VK_SUCCESS) {
|
|
return luaL_error(L, "ui.text: failed to create string");
|
|
}
|
|
|
|
lua_getfield(L, 1, "text");
|
|
if(!lua_isnil(L, -1)) {
|
|
update_ui_string(luaL_checkstring(L, -1), container, slot, ui, gpu);
|
|
}
|
|
lua_pop(L, 1);
|
|
|
|
push_new_handle(L, container, slot, 1);
|
|
return 1;
|
|
}
|
|
|
|
// ---------- handle methods ----------
|
|
|
|
// h:set_pos(x, y)
|
|
static int lua_element_set_pos(lua_State* L) {
|
|
UIElementHandle* h = check_handle(L, 1);
|
|
Container* c = resolve_handle(L, h);
|
|
RenderContext* gpu = registry_pointer(L, UI_LUA_GPU);
|
|
|
|
if(h->is_string) {
|
|
GPUString* s = &c->strings_buffer[h->slot];
|
|
s->pos[0] = luaL_checknumber(L, 2);
|
|
s->pos[1] = luaL_checknumber(L, 3);
|
|
add_transfers(
|
|
&s->pos,
|
|
c->strings,
|
|
h->slot*sizeof(GPUString) + offsetof(GPUString, pos),
|
|
sizeof(vec2),
|
|
gpu);
|
|
} else {
|
|
GPUDrawable* d = &c->drawables_buffer[h->slot];
|
|
d->pos[0] = luaL_checknumber(L, 2);
|
|
d->pos[1] = luaL_checknumber(L, 3);
|
|
add_transfers(
|
|
&d->pos,
|
|
c->drawables,
|
|
h->slot*sizeof(GPUDrawable) + offsetof(GPUDrawable, pos),
|
|
sizeof(vec2),
|
|
gpu);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// h:set_corner_color(corner, r, g, b, a)
|
|
static int lua_element_set_corner_color(lua_State* L) {
|
|
UIElementHandle* h = check_handle(L, 1);
|
|
Container* c = resolve_handle(L, h);
|
|
RenderContext* gpu = registry_pointer(L, UI_LUA_GPU);
|
|
if(h->is_string) {
|
|
return luaL_error(L, "set_corner_color is not valid on text elements");
|
|
}
|
|
uint32_t corner = luaL_checkinteger(L, 2);
|
|
if(corner > 3) {
|
|
return luaL_error(L, "corner %d out of range", corner);
|
|
}
|
|
|
|
GPUDrawable* d = &c->drawables_buffer[h->slot];
|
|
d->color[corner][0] = luaL_checknumber(L, 3);
|
|
d->color[corner][1] = luaL_checknumber(L, 4);
|
|
d->color[corner][2] = luaL_checknumber(L, 5);
|
|
d->color[corner][3] = luaL_checknumber(L, 6);
|
|
|
|
add_transfers(
|
|
&d->color[corner],
|
|
c->drawables,
|
|
h->slot*sizeof(GPUDrawable) + offsetof(GPUDrawable, color) + corner*sizeof(vec4),
|
|
sizeof(vec4),
|
|
gpu);
|
|
return 0;
|
|
}
|
|
|
|
// h:corner_color(corner) -> r, g, b, a
|
|
static int lua_element_corner_color(lua_State* L) {
|
|
UIElementHandle* h = check_handle(L, 1);
|
|
Container* c = resolve_handle(L, h);
|
|
if(h->is_string) {
|
|
return luaL_error(L, "corner_color is not valid on text elements");
|
|
}
|
|
uint32_t corner = luaL_checkinteger(L, 2);
|
|
if(corner > 3) {
|
|
return luaL_error(L, "corner %d out of range", corner);
|
|
}
|
|
|
|
GPUDrawable* d = &c->drawables_buffer[h->slot];
|
|
lua_pushnumber(L, d->color[corner][0]);
|
|
lua_pushnumber(L, d->color[corner][1]);
|
|
lua_pushnumber(L, d->color[corner][2]);
|
|
lua_pushnumber(L, d->color[corner][3]);
|
|
return 4;
|
|
}
|
|
|
|
// h:set_text(text)
|
|
static int lua_element_set_text(lua_State* L) {
|
|
UIElementHandle* h = check_handle(L, 1);
|
|
Container* c = resolve_handle(L, h);
|
|
UIContext* ui = registry_pointer(L, UI_LUA_CONTEXT);
|
|
RenderContext* gpu = registry_pointer(L, UI_LUA_GPU);
|
|
if(!h->is_string) {
|
|
return luaL_error(L, "set_text is not valid on rect elements");
|
|
}
|
|
|
|
update_ui_string(luaL_checkstring(L, 2), c, h->slot, ui, gpu);
|
|
return 0;
|
|
}
|
|
|
|
// h:set_z(z)
|
|
static int lua_element_set_z(lua_State* L) {
|
|
UIElementHandle* h = check_handle(L, 1);
|
|
Container* c = resolve_handle(L, h);
|
|
RenderContext* gpu = registry_pointer(L, UI_LUA_GPU);
|
|
float z = luaL_checknumber(L, 2);
|
|
|
|
if(h->is_string) {
|
|
ui_set_string_z(c, h->slot, z, gpu);
|
|
} else {
|
|
ui_set_drawable_z(c, h->slot, z, gpu);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// h:focus()
|
|
static int lua_element_focus(lua_State* L) {
|
|
UIElementHandle* h = check_handle(L, 1);
|
|
Container* c = resolve_handle(L, h);
|
|
UIContext* ui = registry_pointer(L, UI_LUA_CONTEXT);
|
|
if(h->is_string) {
|
|
return luaL_error(L, "focus is not valid on text elements");
|
|
}
|
|
|
|
set_active_element(c->id, h->slot, ui);
|
|
return 0;
|
|
}
|
|
|
|
// h:is_focused() -> bool
|
|
static int lua_element_is_focused(lua_State* L) {
|
|
UIElementHandle* h = check_handle(L, 1);
|
|
Container* c = resolve_handle(L, h);
|
|
UIContext* ui = registry_pointer(L, UI_LUA_CONTEXT);
|
|
|
|
lua_pushboolean(L,
|
|
h->is_string == 0
|
|
&& ui->active_container == c
|
|
&& ui->active_element == h->slot);
|
|
return 1;
|
|
}
|
|
|
|
// h:remove()
|
|
static int lua_element_remove(lua_State* L) {
|
|
UIElementHandle* h = check_handle(L, 1);
|
|
Container* c = resolve_handle(L, h);
|
|
RenderContext* gpu = registry_pointer(L, UI_LUA_GPU);
|
|
|
|
if(h->is_string) {
|
|
luaL_unref(L, LUA_REGISTRYINDEX, c->string_ref[h->slot]);
|
|
c->string_ref[h->slot] = LUA_NOREF;
|
|
ui_destroy_string(c, h->slot, gpu);
|
|
} else {
|
|
luaL_unref(L, LUA_REGISTRYINDEX, c->drawable_ref[h->slot]);
|
|
c->drawable_ref[h->slot] = LUA_NOREF;
|
|
ui_destroy_drawable(c, h->slot, gpu);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// ---------- ui table functions ----------
|
|
|
|
// ui.blur()
|
|
static int lua_ui_blur(lua_State* L) {
|
|
UIContext* ui = registry_pointer(L, UI_LUA_CONTEXT);
|
|
RenderContext* gpu = registry_pointer(L, UI_LUA_GPU);
|
|
|
|
clear_active_element(ui, gpu);
|
|
return 0;
|
|
}
|
|
|
|
// ui.raise() — bring the calling script's container to the front
|
|
static int lua_ui_raise(lua_State* L) {
|
|
Container* container = registry_pointer(L, UI_LUA_CONTAINER);
|
|
UIContext* ui = registry_pointer(L, UI_LUA_CONTEXT);
|
|
|
|
ui_container_to_front(container->id, ui);
|
|
return 0;
|
|
}
|
|
|
|
// ui.hsv_to_rgb(h, s, v) -> r, g, b
|
|
static int lua_ui_hsv_to_rgb(lua_State* L) {
|
|
double hsv[3] = {
|
|
luaL_checknumber(L, 1),
|
|
luaL_checknumber(L, 2),
|
|
luaL_checknumber(L, 3),
|
|
};
|
|
vec3 rgb;
|
|
hsv_to_rgb(hsv, rgb);
|
|
lua_pushnumber(L, rgb[0]);
|
|
lua_pushnumber(L, rgb[1]);
|
|
lua_pushnumber(L, rgb[2]);
|
|
return 3;
|
|
}
|
|
|
|
// ui.rgb_to_hsv(r, g, b) -> h, s, v
|
|
static int lua_ui_rgb_to_hsv(lua_State* L) {
|
|
vec3 rgb = {
|
|
luaL_checknumber(L, 1),
|
|
luaL_checknumber(L, 2),
|
|
luaL_checknumber(L, 3),
|
|
};
|
|
double hsv[3];
|
|
rgb_to_hsv(rgb, hsv);
|
|
lua_pushnumber(L, hsv[0]);
|
|
lua_pushnumber(L, hsv[1]);
|
|
lua_pushnumber(L, hsv[2]);
|
|
return 3;
|
|
}
|
|
|
|
void ui_lua_register(lua_State* L) {
|
|
static const luaL_Reg ui_funcs[] = {
|
|
{"rect", lua_ui_rect},
|
|
{"text", lua_ui_text},
|
|
{"blur", lua_ui_blur},
|
|
{"raise", lua_ui_raise},
|
|
{"hsv_to_rgb", lua_ui_hsv_to_rgb},
|
|
{"rgb_to_hsv", lua_ui_rgb_to_hsv},
|
|
{NULL, NULL},
|
|
};
|
|
luaL_newlib(L, ui_funcs);
|
|
lua_setglobal(L, "ui");
|
|
|
|
static const luaL_Reg element_methods[] = {
|
|
{"set_pos", lua_element_set_pos},
|
|
{"set_corner_color", lua_element_set_corner_color},
|
|
{"corner_color", lua_element_corner_color},
|
|
{"set_text", lua_element_set_text},
|
|
{"set_z", lua_element_set_z},
|
|
{"focus", lua_element_focus},
|
|
{"is_focused", lua_element_is_focused},
|
|
{"remove", lua_element_remove},
|
|
{NULL, NULL},
|
|
};
|
|
luaL_newmetatable(L, UI_LUA_ELEMENT_META);
|
|
luaL_newlib(L, element_methods);
|
|
lua_setfield(L, -2, "__index");
|
|
lua_pop(L, 1);
|
|
|
|
lua_pushinteger(L, GLFW_PRESS); lua_setglobal(L, "PRESS");
|
|
lua_pushinteger(L, GLFW_RELEASE); lua_setglobal(L, "RELEASE");
|
|
lua_pushinteger(L, GLFW_MOUSE_BUTTON_LEFT); lua_setglobal(L, "MOUSE_LEFT");
|
|
lua_pushinteger(L, GLFW_MOUSE_BUTTON_RIGHT); lua_setglobal(L, "MOUSE_RIGHT");
|
|
lua_pushinteger(L, GLFW_MOUSE_BUTTON_MIDDLE); lua_setglobal(L, "MOUSE_MIDDLE");
|
|
lua_pushinteger(L, GLFW_KEY_ESCAPE); lua_setglobal(L, "KEY_ESCAPE");
|
|
lua_pushinteger(L, GLFW_KEY_ENTER); lua_setglobal(L, "KEY_ENTER");
|
|
lua_pushinteger(L, GLFW_KEY_BACKSPACE); lua_setglobal(L, "KEY_BACKSPACE");
|
|
|
|
lua_pushinteger(L, DRAWABLE_TYPE_RECT); lua_setglobal(L, "RECT");
|
|
lua_pushinteger(L, DRAWABLE_TYPE_RECT_HSV); lua_setglobal(L, "RECT_HSV");
|
|
lua_pushinteger(L, DRAWABLE_TYPE_IMAGE); lua_setglobal(L, "IMAGE");
|
|
lua_pushinteger(L, UI_EVENT_BUTTON); lua_setglobal(L, "EVENT_BUTTON");
|
|
lua_pushinteger(L, UI_EVENT_SCROLL); lua_setglobal(L, "EVENT_SCROLL");
|
|
lua_pushinteger(L, UI_EVENT_CURSOR); lua_setglobal(L, "EVENT_CURSOR");
|
|
}
|
|
|
|
VkResult ui_lua_load_container(
|
|
lua_State* L,
|
|
Container* c,
|
|
UIContext* ui,
|
|
RenderContext* gpu,
|
|
const char* path) {
|
|
|
|
if(luaL_loadfile(L, path) != LUA_OK) {
|
|
fprintf(stderr, "ui_lua_load_container: %s\n", lua_tostring(L, -1));
|
|
lua_pop(L, 1);
|
|
return VK_ERROR_UNKNOWN;
|
|
}
|
|
|
|
// Sandboxed environment table: env = setmetatable({}, {__index = _G}),
|
|
// set as the chunk's _ENV so script globals land in env, not _G
|
|
lua_newtable(L);
|
|
lua_newtable(L);
|
|
lua_getglobal(L, "_G");
|
|
lua_setfield(L, -2, "__index");
|
|
lua_setmetatable(L, -2);
|
|
|
|
lua_pushvalue(L, -1);
|
|
lua_setupvalue(L, -3, 1);
|
|
|
|
// Ref the env first so the chunk (now at the top) can be called
|
|
c->script_env = luaL_ref(L, LUA_REGISTRYINDEX);
|
|
|
|
// Top-level script code declares elements via ui.* during execution
|
|
set_registry_pointers(L, c, ui, gpu);
|
|
|
|
if(lua_pcall(L, 0, 0, 0) != LUA_OK) {
|
|
fprintf(stderr, "ui_lua_load_container: %s\n", lua_tostring(L, -1));
|
|
lua_pop(L, 1);
|
|
luaL_unref(L, LUA_REGISTRYINDEX, c->script_env);
|
|
c->script_env = 0;
|
|
return VK_ERROR_UNKNOWN;
|
|
}
|
|
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
// Pushes the container's handler onto the stack, ready for args.
|
|
// Returns false if the container has no script or no such handler.
|
|
static bool ui_lua_begin_dispatch(
|
|
lua_State* L,
|
|
UIContext* ui,
|
|
RenderContext* gpu,
|
|
Container* c,
|
|
const char* handler) {
|
|
if(c == NULL || c->script_env == 0) return false;
|
|
|
|
set_registry_pointers(L, c, ui, gpu);
|
|
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, c->script_env);
|
|
lua_getfield(L, -1, handler);
|
|
lua_remove(L, -2);
|
|
if(!lua_isfunction(L, -1)) {
|
|
lua_pop(L, 1);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Calls the handler with nargs already on the stack, returns its boolean result
|
|
static bool ui_lua_finish_dispatch(lua_State* L, const char* handler, int nargs) {
|
|
if(lua_pcall(L, nargs, 1, 0) != LUA_OK) {
|
|
fprintf(stderr, "%s: %s\n", handler, lua_tostring(L, -1));
|
|
lua_pop(L, 1);
|
|
return false;
|
|
}
|
|
bool consumed = lua_toboolean(L, -1);
|
|
lua_pop(L, 1);
|
|
return consumed;
|
|
}
|
|
|
|
VkResult ui_lua_call(
|
|
UIContext* ui,
|
|
RenderContext* gpu,
|
|
Container* c,
|
|
const char* function,
|
|
const char* argument) {
|
|
lua_State* L = ui->lua;
|
|
if(!ui_lua_begin_dispatch(L, ui, gpu, c, function)) {
|
|
return VK_ERROR_UNKNOWN;
|
|
}
|
|
lua_pushstring(L, argument);
|
|
if(lua_pcall(L, 1, 0, 0) != LUA_OK) {
|
|
fprintf(stderr, "%s: %s\n", function, lua_tostring(L, -1));
|
|
lua_pop(L, 1);
|
|
return VK_ERROR_UNKNOWN;
|
|
}
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
bool ui_lua_dispatch_button(
|
|
UIContext* ui,
|
|
RenderContext* gpu,
|
|
Container* c,
|
|
uint32_t element,
|
|
float x,
|
|
float y,
|
|
int button,
|
|
int action,
|
|
int mods) {
|
|
lua_State* L = ui->lua;
|
|
if(!ui_lua_begin_dispatch(L, ui, gpu, c, "on_button")) return false;
|
|
push_element_handle(L, c, element);
|
|
lua_pushnumber(L, x);
|
|
lua_pushnumber(L, y);
|
|
lua_pushinteger(L, button);
|
|
lua_pushinteger(L, action);
|
|
lua_pushinteger(L, mods);
|
|
return ui_lua_finish_dispatch(L, "on_button", 6);
|
|
}
|
|
|
|
bool ui_lua_dispatch_cursor(
|
|
UIContext* ui,
|
|
RenderContext* gpu,
|
|
Container* c,
|
|
uint32_t element,
|
|
float x,
|
|
float y) {
|
|
lua_State* L = ui->lua;
|
|
if(!ui_lua_begin_dispatch(L, ui, gpu, c, "on_cursor")) return false;
|
|
push_element_handle(L, c, element);
|
|
lua_pushnumber(L, x);
|
|
lua_pushnumber(L, y);
|
|
return ui_lua_finish_dispatch(L, "on_cursor", 3);
|
|
}
|
|
|
|
bool ui_lua_dispatch_scroll(
|
|
UIContext* ui,
|
|
RenderContext* gpu,
|
|
Container* c,
|
|
uint32_t element,
|
|
double x,
|
|
double y) {
|
|
lua_State* L = ui->lua;
|
|
if(!ui_lua_begin_dispatch(L, ui, gpu, c, "on_scroll")) return false;
|
|
push_element_handle(L, c, element);
|
|
lua_pushnumber(L, x);
|
|
lua_pushnumber(L, y);
|
|
return ui_lua_finish_dispatch(L, "on_scroll", 3);
|
|
}
|
|
|
|
bool ui_lua_dispatch_key(
|
|
UIContext* ui,
|
|
RenderContext* gpu,
|
|
Container* c,
|
|
uint32_t element,
|
|
int key,
|
|
int action,
|
|
int mods) {
|
|
lua_State* L = ui->lua;
|
|
if(!ui_lua_begin_dispatch(L, ui, gpu, c, "on_key")) return false;
|
|
push_element_handle(L, c, element);
|
|
lua_pushinteger(L, key);
|
|
lua_pushinteger(L, action);
|
|
lua_pushinteger(L, mods);
|
|
return ui_lua_finish_dispatch(L, "on_key", 4);
|
|
}
|
|
|
|
bool ui_lua_dispatch_text(
|
|
UIContext* ui,
|
|
RenderContext* gpu,
|
|
Container* c,
|
|
uint32_t element,
|
|
unsigned int codepoint) {
|
|
lua_State* L = ui->lua;
|
|
if(!ui_lua_begin_dispatch(L, ui, gpu, c, "on_text")) return false;
|
|
push_element_handle(L, c, element);
|
|
lua_pushinteger(L, codepoint);
|
|
return ui_lua_finish_dispatch(L, "on_text", 2);
|
|
}
|
|
|
|
bool ui_lua_dispatch_deselect(
|
|
UIContext* ui,
|
|
RenderContext* gpu,
|
|
Container* c,
|
|
uint32_t element) {
|
|
lua_State* L = ui->lua;
|
|
if(!ui_lua_begin_dispatch(L, ui, gpu, c, "on_deselect")) return false;
|
|
push_element_handle(L, c, element);
|
|
return ui_lua_finish_dispatch(L, "on_deselect", 1);
|
|
}
|