library: better and fuller mouse event system

This commit is contained in:
Adam
2024-06-22 17:16:22 +01:00
parent e1bb14f7e7
commit 0f331d241b
5 changed files with 518 additions and 258 deletions

View File

@@ -1105,7 +1105,7 @@ void* _bolt_gl_GetProcAddress(const char* name) {
void _bolt_gl_onSwapBuffers(uint32_t window_width, uint32_t window_height) {
gl_width = window_width;
gl_height = window_height;
_bolt_plugin_process_windows(window_width, window_height);
if (_bolt_plugin_is_inited()) _bolt_plugin_process_windows(window_width, window_height);
}
void _bolt_gl_onCreateContext(void* context, void* shared_context, const struct GLLibFunctions* libgl, void* (*GetProcAddress)(const char*)) {

View File

@@ -29,11 +29,19 @@
#define MINIMAP_META_REGISTRYNAME "minimapmeta"
#define SWAPBUFFERS_META_REGISTRYNAME "swapbuffersmeta"
#define SURFACE_META_REGISTRYNAME "surfacemeta"
#define RESIZE_META_REGISTRYNAME "resizemeta"
#define MOUSEMOTION_META_REGISTRYNAME "mousemotionmeta"
#define MOUSEBUTTON_META_REGISTRYNAME "mousebuttonmeta"
#define SCROLL_META_REGISTRYNAME "scrollmeta"
#define WINDOW_META_REGISTRYNAME "windowmeta"
#define WINDOWLIGHT_META_REGISTRYNAME "windowlightmeta"
#define SWAPBUFFERS_CB_REGISTRYNAME "swapbufferscb"
#define BATCH2D_CB_REGISTRYNAME "batch2dcb"
#define RENDER3D_CB_REGISTRYNAME "render3dcb"
#define MINIMAP_CB_REGISTRYNAME "minimapcb"
#define MOUSEMOTION_CB_REGISTRYNAME "mousemotioncb"
#define MOUSEBUTTON_CB_REGISTRYNAME "mousebuttoncb"
#define SCROLL_CB_REGISTRYNAME "scrollcb"
enum {
WINDOW_ONRESIZE,
@@ -43,6 +51,23 @@ enum {
WINDOW_EVENT_ENUM_SIZE, // last member of enum
};
struct ResizeEvent {
uint16_t width;
uint16_t height;
};
struct MouseMotionEvent {
struct MouseEvent* details;
};
struct MouseButtonEvent {
struct MouseEvent* details;
uint8_t button; // 1 left, 2 right, 3 middle
};
struct MouseScrollEvent {
struct MouseEvent* details;
uint8_t direction; // 0 down, 1 up
};
static struct PluginManagedFunctions managed_functions;
static uint64_t next_window_id;
@@ -64,6 +89,14 @@ struct Plugin {
uint32_t path_length;
};
static void _bolt_plugin_window_onresize(struct EmbeddedWindow*, struct ResizeEvent*);
static void _bolt_plugin_window_onmousemotion(struct EmbeddedWindow*, struct MouseMotionEvent*);
static void _bolt_plugin_window_onmousebutton(struct EmbeddedWindow*, struct MouseButtonEvent*);
static void _bolt_plugin_window_onscroll(struct EmbeddedWindow*, struct MouseScrollEvent*);
static void _bolt_plugin_handle_mousemotion(struct MouseMotionEvent*);
static void _bolt_plugin_handle_mousebutton(struct MouseButtonEvent*);
static void _bolt_plugin_handle_scroll(struct MouseScrollEvent*);
void _bolt_plugin_free(struct Plugin* const* plugin) {
lua_close((*plugin)->state);
free((*plugin)->id);
@@ -133,9 +166,9 @@ static int api_setcallback##APINAME(lua_State* state) { \
return 0; \
}
// macro for defining function "api_window_on*"
// e.g. DEFINE_WINDOWEVENT(resize, RESIZE)
#define DEFINE_WINDOWEVENT(APINAME, REGNAME) \
// macro for defining function "api_window_on*" and "_bolt_plugin_window_on*"
// e.g. DEFINE_WINDOWEVENT(resize, RESIZE, ResizeEvent)
#define DEFINE_WINDOWEVENT(APINAME, REGNAME, EVNAME) \
static int api_window_on##APINAME(lua_State* state) { \
_bolt_check_argc(state, 2, "window_on"#APINAME); \
const struct EmbeddedWindow* window = lua_touserdata(state, 1); \
@@ -151,6 +184,34 @@ static int api_window_on##APINAME(lua_State* state) { \
lua_settable(state, -3); /*stack: window table, event table*/ \
lua_pop(state, 2); /*stack: (empty)*/ \
return 0; \
} \
void _bolt_plugin_window_on##APINAME(struct EmbeddedWindow* window, struct EVNAME* event) { \
lua_State* state = window->plugin; \
lua_getfield(state, LUA_REGISTRYINDEX, WINDOWS_REGISTRYNAME); /*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, WINDOW_ON##REGNAME); /*stack: window table, event table, event id*/ \
lua_gettable(state, -2); /*stack: window table, event table, function or nil*/ \
if (lua_isfunction(state, -1)) { \
lua_pushlightuserdata(state, window); /*stack: window table, event table, function, window*/ \
lua_getfield(state, LUA_REGISTRYINDEX, WINDOWLIGHT_META_REGISTRYNAME); /*stack: window table, event table, function, window, window metatable*/ \
lua_setmetatable(state, -2); /*stack: window table, event table, function, window*/ \
lua_pushlightuserdata(state, event); /*stack: window table, event table, function, window, event*/ \
lua_getfield(state, LUA_REGISTRYINDEX, REGNAME##_META_REGISTRYNAME); /*stack: window table, event table, function, window, event, event metatable*/ \
lua_setmetatable(state, -2); /*stack: window table, event table, function, window, event*/ \
if (lua_pcall(state, 2, 0, 0)) { /*stack: window table, event table, ?error*/ \
const char* e = lua_tolstring(state, -1, 0); \
printf("plugin window on" #APINAME " error: %s\n", e); \
lua_getfield(state, LUA_REGISTRYINDEX, PLUGIN_REGISTRYNAME); /*stack: window table, event table, error, plugin*/ \
const struct Plugin* plugin = lua_touserdata(state, -1); \
lua_pop(state, 4); /*stack: (empty)*/ \
_bolt_plugin_stop(plugin->id, plugin->id_length); \
} else { \
lua_pop(state, 2); /*stack: (empty)*/ \
} \
} else { \
lua_pop(state, 3); \
} \
}
static int surface_gc(lua_State* state) {
@@ -227,72 +288,112 @@ uint8_t _bolt_plugin_is_inited() {
}
void _bolt_plugin_process_windows(uint32_t window_width, uint32_t window_height) {
if (_bolt_plugin_is_inited()) {
struct SwapBuffersEvent event;
_bolt_plugin_handle_swapbuffers(&event);
_bolt_plugin_handle_messages();
struct WindowInfo* windows = _bolt_plugin_windowinfo();
_bolt_rwlock_lock_read(&windows->lock);
size_t iter = 0;
void* item;
while (hashmap_iter(windows->map, &iter, &item)) {
struct EmbeddedWindow* window = *(struct EmbeddedWindow**)item;
_bolt_rwlock_lock_write(&window->lock);
struct EmbeddedWindowMetadata metadata = window->metadata;
bool did_resize = false;
if (metadata.width > window_width) {
metadata.width = window_width;
did_resize = true;
}
if (metadata.height > window_height) {
metadata.height = window_height;
did_resize = true;
}
if (metadata.x < 0) {
metadata.x = 0;
}
if (metadata.y < 0) {
metadata.y = 0;
}
if (metadata.x + metadata.width > window_width) {
metadata.x = window_width - metadata.width;
}
if (metadata.y + metadata.height > window_height) {
metadata.y = window_height - metadata.height;
}
window->metadata = metadata;
memset(&window->metadata.input, 0, sizeof(window->metadata.input));
_bolt_rwlock_unlock_write(&window->lock);
struct SwapBuffersEvent event;
_bolt_plugin_handle_swapbuffers(&event);
_bolt_plugin_handle_messages();
struct WindowInfo* windows = _bolt_plugin_windowinfo();
if (did_resize) {
struct PluginSurfaceUserdata* ud = window->surface_functions.userdata;
managed_functions.surface_resize_and_clear(ud, metadata.width, metadata.height);
_bolt_plugin_window_onresize(window, metadata.width, metadata.height);
}
_bolt_rwlock_lock_write(&windows->input_lock);
struct WindowPendingInput inputs = windows->input;
memset(&windows->input, 0, sizeof(windows->input));
_bolt_rwlock_unlock_write(&windows->input_lock);
if (metadata.input.mouse_motion) {
_bolt_plugin_window_onmousemotion(window, metadata.input.motion_x, metadata.input.motion_y);
}
if (metadata.input.left_click) {
_bolt_plugin_window_onmousebutton(window, MBLeft, metadata.input.left_click_x, metadata.input.left_click_y);
}
if (metadata.input.right_click) {
_bolt_plugin_window_onmousebutton(window, MBRight, metadata.input.right_click_x, metadata.input.right_click_y);
}
if (metadata.input.middle_click) {
_bolt_plugin_window_onmousebutton(window, MBMiddle, metadata.input.middle_click_x, metadata.input.middle_click_y);
}
if (metadata.input.scroll_up) {
_bolt_plugin_window_onscroll(window, 1);
}
if (metadata.input.scroll_down) {
_bolt_plugin_window_onscroll(window, 0);
}
window->surface_functions.draw_to_screen(window->surface_functions.userdata, 0, 0, metadata.width, metadata.height, metadata.x, metadata.y, metadata.width, metadata.height);
}
_bolt_rwlock_unlock_read(&windows->lock);
if (inputs.mouse_motion) {
struct MouseMotionEvent event = {.details = &inputs.mouse_motion_event};
_bolt_plugin_handle_mousemotion(&event);
}
if (inputs.mouse_left) {
struct MouseButtonEvent event = {.details = &inputs.mouse_left_event, .button = MBLeft};
_bolt_plugin_handle_mousebutton(&event);
}
if (inputs.mouse_right) {
struct MouseButtonEvent event = {.details = &inputs.mouse_right_event, .button = MBRight};
_bolt_plugin_handle_mousebutton(&event);
}
if (inputs.mouse_middle) {
struct MouseButtonEvent event = {.details = &inputs.mouse_middle_event, .button = MBMiddle};
_bolt_plugin_handle_mousebutton(&event);
}
if (inputs.mouse_scroll_up) {
struct MouseScrollEvent event = {.details = &inputs.mouse_scroll_up_event, .direction = 1};
_bolt_plugin_handle_scroll(&event);
}
if (inputs.mouse_scroll_down) {
struct MouseScrollEvent event = {.details = &inputs.mouse_scroll_up_event, .direction = 0};
_bolt_plugin_handle_scroll(&event);
}
_bolt_rwlock_lock_read(&windows->lock);
size_t iter = 0;
void* item;
while (hashmap_iter(windows->map, &iter, &item)) {
struct EmbeddedWindow* window = *(struct EmbeddedWindow**)item;
_bolt_rwlock_lock_write(&window->lock);
bool did_resize = false;
if (window->metadata.width > window_width) {
window->metadata.width = window_width;
did_resize = true;
}
if (window->metadata.height > window_height) {
window->metadata.height = window_height;
did_resize = true;
}
if (window->metadata.x < 0) {
window->metadata.x = 0;
}
if (window->metadata.y < 0) {
window->metadata.y = 0;
}
if (window->metadata.x + window->metadata.width > window_width) {
window->metadata.x = window_width - window->metadata.width;
}
if (window->metadata.y + window->metadata.height > window_height) {
window->metadata.y = window_height - window->metadata.height;
}
struct EmbeddedWindowMetadata metadata = window->metadata;
_bolt_rwlock_unlock_write(&window->lock);
_bolt_rwlock_lock_write(&window->input_lock);
struct WindowPendingInput inputs = window->input;
memset(&window->input, 0, sizeof(window->input));
_bolt_rwlock_unlock_write(&window->input_lock);
if (did_resize) {
struct PluginSurfaceUserdata* ud = window->surface_functions.userdata;
managed_functions.surface_resize_and_clear(ud, metadata.width, metadata.height);
struct ResizeEvent event = {.width = metadata.width, .height = metadata.height};
_bolt_plugin_window_onresize(window, &event);
}
if (inputs.mouse_motion) {
struct MouseMotionEvent event = {.details = &inputs.mouse_motion_event};
_bolt_plugin_window_onmousemotion(window, &event);
}
if (inputs.mouse_left) {
struct MouseButtonEvent event = {.details = &inputs.mouse_left_event, .button = MBLeft};
_bolt_plugin_window_onmousebutton(window, &event);
}
if (inputs.mouse_right) {
struct MouseButtonEvent event = {.details = &inputs.mouse_right_event, .button = MBRight};
_bolt_plugin_window_onmousebutton(window, &event);
}
if (inputs.mouse_middle) {
struct MouseButtonEvent event = {.details = &inputs.mouse_middle_event, .button = MBMiddle};
_bolt_plugin_window_onmousebutton(window, &event);
}
if (inputs.mouse_scroll_up) {
struct MouseScrollEvent event = {.details = &inputs.mouse_scroll_up_event, .direction = 1};
_bolt_plugin_window_onscroll(window, &event);
}
if (inputs.mouse_scroll_down) {
struct MouseScrollEvent event = {.details = &inputs.mouse_scroll_up_event, .direction = 0};
_bolt_plugin_window_onscroll(window, &event);
}
window->surface_functions.draw_to_screen(window->surface_functions.userdata, 0, 0, metadata.width, metadata.height, metadata.x, metadata.y, metadata.width, metadata.height);
}
_bolt_rwlock_unlock_read(&windows->lock);
}
void _bolt_plugin_close() {
@@ -497,21 +598,82 @@ uint8_t _bolt_plugin_add(const char* path, struct Plugin* plugin) {
lua_settable(plugin->state, -3);
lua_settable(plugin->state, LUA_REGISTRYINDEX);
// create the metatable for all Window objects
PUSHSTRING(plugin->state, WINDOW_META_REGISTRYNAME);
// create both of the metatables for Window objects
for (size_t i = 0; i <= 1; i += 1) {
if (i == 0) PUSHSTRING(plugin->state, WINDOW_META_REGISTRYNAME);
else PUSHSTRING(plugin->state, WINDOWLIGHT_META_REGISTRYNAME);
lua_newtable(plugin->state);
PUSHSTRING(plugin->state, "__index");
lua_createtable(plugin->state, 0, 7);
API_ADD_SUB(plugin->state, id, window)
API_ADD_SUB(plugin->state, size, window)
API_ADD_SUB(plugin->state, clear, window)
API_ADD_SUB(plugin->state, onresize, window)
API_ADD_SUB(plugin->state, onmousemotion, window)
API_ADD_SUB(plugin->state, onmousebutton, window)
API_ADD_SUB(plugin->state, onscroll, window)
lua_settable(plugin->state, -3);
if (i == 0) {
PUSHSTRING(plugin->state, "__gc");
lua_pushcfunction(plugin->state, window_gc);
lua_settable(plugin->state, -3);
}
lua_settable(plugin->state, LUA_REGISTRYINDEX);
}
// create the metatable for all ResizeEvent objects
PUSHSTRING(plugin->state, RESIZE_META_REGISTRYNAME);
lua_newtable(plugin->state);
PUSHSTRING(plugin->state, "__index");
lua_createtable(plugin->state, 0, 1);
API_ADD_SUB(plugin->state, size, resizeevent)
lua_settable(plugin->state, -3);
lua_settable(plugin->state, LUA_REGISTRYINDEX);
// create the metatable for all MouseMotionEvent objects
PUSHSTRING(plugin->state, MOUSEMOTION_META_REGISTRYNAME);
lua_newtable(plugin->state);
PUSHSTRING(plugin->state, "__index");
lua_createtable(plugin->state, 0, 7);
API_ADD_SUB(plugin->state, id, window)
API_ADD_SUB(plugin->state, size, window)
API_ADD_SUB(plugin->state, clear, window)
API_ADD_SUB(plugin->state, onresize, window)
API_ADD_SUB(plugin->state, onmousemotion, window)
API_ADD_SUB(plugin->state, onmousebutton, window)
API_ADD_SUB(plugin->state, onscroll, window)
API_ADD_SUB(plugin->state, xy, mouseevent)
API_ADD_SUB(plugin->state, ctrl, mouseevent);
API_ADD_SUB(plugin->state, shift, mouseevent);
API_ADD_SUB(plugin->state, meta, mouseevent);
API_ADD_SUB(plugin->state, alt, mouseevent);
API_ADD_SUB(plugin->state, capslock, mouseevent);
API_ADD_SUB(plugin->state, numlock, mouseevent);
lua_settable(plugin->state, -3);
PUSHSTRING(plugin->state, "__gc");
lua_pushcfunction(plugin->state, window_gc);
lua_settable(plugin->state, LUA_REGISTRYINDEX);
// create the metatable for all MouseButtonEvent objects
PUSHSTRING(plugin->state, MOUSEBUTTON_META_REGISTRYNAME);
lua_newtable(plugin->state);
PUSHSTRING(plugin->state, "__index");
lua_createtable(plugin->state, 0, 8);
API_ADD_SUB(plugin->state, xy, mouseevent)
API_ADD_SUB(plugin->state, ctrl, mouseevent);
API_ADD_SUB(plugin->state, shift, mouseevent);
API_ADD_SUB(plugin->state, meta, mouseevent);
API_ADD_SUB(plugin->state, alt, mouseevent);
API_ADD_SUB(plugin->state, capslock, mouseevent);
API_ADD_SUB(plugin->state, numlock, mouseevent);
API_ADD_SUB(plugin->state, button, mousebutton);
lua_settable(plugin->state, -3);
lua_settable(plugin->state, LUA_REGISTRYINDEX);
// create the metatable for all MouseScrollEvent objects
PUSHSTRING(plugin->state, SCROLL_META_REGISTRYNAME);
lua_newtable(plugin->state);
PUSHSTRING(plugin->state, "__index");
lua_createtable(plugin->state, 0, 8);
API_ADD_SUB(plugin->state, xy, mouseevent)
API_ADD_SUB(plugin->state, ctrl, mouseevent);
API_ADD_SUB(plugin->state, shift, mouseevent);
API_ADD_SUB(plugin->state, meta, mouseevent);
API_ADD_SUB(plugin->state, alt, mouseevent);
API_ADD_SUB(plugin->state, capslock, mouseevent);
API_ADD_SUB(plugin->state, numlock, mouseevent);
API_ADD_SUB(plugin->state, direction, scroll);
lua_settable(plugin->state, -3);
lua_settable(plugin->state, LUA_REGISTRYINDEX);
@@ -548,122 +710,13 @@ DEFINE_CALLBACK(swapbuffers, SWAPBUFFERS, SwapBuffersEvent)
DEFINE_CALLBACK(2d, BATCH2D, RenderBatch2D)
DEFINE_CALLBACK(3d, RENDER3D, Render3D)
DEFINE_CALLBACK(minimap, MINIMAP, RenderMinimapEvent)
DEFINE_WINDOWEVENT(resize, RESIZE)
DEFINE_WINDOWEVENT(mousemotion, MOUSEMOTION)
DEFINE_WINDOWEVENT(mousebutton, MOUSEBUTTON)
DEFINE_WINDOWEVENT(scroll, SCROLL)
void _bolt_plugin_window_onresize(struct EmbeddedWindow* window, int width, int height) {
lua_State* state = window->plugin;
lua_getfield(state, LUA_REGISTRYINDEX, WINDOWS_REGISTRYNAME); /*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, WINDOW_ONRESIZE); /*stack: window table, event table, event id*/
lua_gettable(state, -2); /*stack: window table, event table, function or nil*/
if (lua_isfunction(state, -1)) {
lua_pushlightuserdata(state, window); /*stack: window table, event table, function, window*/
lua_getfield(state, LUA_REGISTRYINDEX, WINDOW_META_REGISTRYNAME); /*stack: window table, event table, function, window, window metatable*/
lua_setmetatable(state, -2); /*stack: window table, event table, function, window*/
lua_pushinteger(state, width); /*stack: window table, event table, function, window, width*/
lua_pushinteger(state, height); /*stack: window table, event table, function, window, width, height*/
if (lua_pcall(state, 3, 0, 0)) { /*stack: window table, event table, ?error*/
const char* e = lua_tolstring(state, -1, 0);
printf("plugin window onresize error: %s\n", e);
lua_getfield(state, LUA_REGISTRYINDEX, PLUGIN_REGISTRYNAME); /*stack: window table, event table, error, plugin*/
const struct Plugin* plugin = lua_touserdata(state, -1);
lua_pop(state, 4); /*stack: (empty)*/
_bolt_plugin_stop(plugin->id, plugin->id_length);
} else {
lua_pop(state, 2); /*stack: (empty)*/
}
} else {
lua_pop(state, 3);
}
}
void _bolt_plugin_window_onmousemotion(struct EmbeddedWindow* window, int x, int y) {
lua_State* state = window->plugin;
lua_getfield(state, LUA_REGISTRYINDEX, WINDOWS_REGISTRYNAME); /*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, WINDOW_ONMOUSEMOTION); /*stack: window table, event table, event id*/
lua_gettable(state, -2); /*stack: window table, event table, function or nil*/
if (lua_isfunction(state, -1)) {
lua_pushlightuserdata(state, window); /*stack: window table, event table, function, window*/
lua_getfield(state, LUA_REGISTRYINDEX, WINDOW_META_REGISTRYNAME); /*stack: window table, event table, function, window, window metatable*/
lua_setmetatable(state, -2); /*stack: window table, event table, function, window*/
lua_pushinteger(state, x); /*stack: window table, event table, function, window, x*/
lua_pushinteger(state, y); /*stack: window table, event table, function, window, x, y*/
if (lua_pcall(state, 3, 0, 0)) { /*stack: window table, event table, ?error*/
const char* e = lua_tolstring(state, -1, 0);
printf("plugin window onmousemotion error: %s\n", e);
lua_getfield(state, LUA_REGISTRYINDEX, PLUGIN_REGISTRYNAME); /*stack: window table, event table, error, plugin*/
const struct Plugin* plugin = lua_touserdata(state, -1);
lua_pop(state, 4); /*stack: (empty)*/
_bolt_plugin_stop(plugin->id, plugin->id_length);
} else {
lua_pop(state, 2); /*stack: (empty)*/
}
} else {
lua_pop(state, 3);
}
}
void _bolt_plugin_window_onmousebutton(struct EmbeddedWindow* window, enum PluginMouseButton button, int x, int y) {
lua_State* state = window->plugin;
lua_getfield(state, LUA_REGISTRYINDEX, WINDOWS_REGISTRYNAME); /*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, WINDOW_ONMOUSEBUTTON); /*stack: window table, event table, event id*/
lua_gettable(state, -2); /*stack: window table, event table, function or nil*/
if (lua_isfunction(state, -1)) {
lua_pushlightuserdata(state, window); /*stack: window table, event table, function, window*/
lua_getfield(state, LUA_REGISTRYINDEX, WINDOW_META_REGISTRYNAME); /*stack: window table, event table, function, window, window metatable*/
lua_setmetatable(state, -2); /*stack: window table, event table, function, window*/
lua_pushinteger(state, button); /*stack: window table, event table, function, window, button*/
lua_pushinteger(state, x); /*stack: window table, event table, function, window, button, x*/
lua_pushinteger(state, y); /*stack: window table, event table, function, window, button, x, y*/
if (lua_pcall(state, 4, 0, 0)) { /*stack: window table, event table, ?error*/
const char* e = lua_tolstring(state, -1, 0);
printf("plugin window onmousebutton error: %s\n", e);
lua_getfield(state, LUA_REGISTRYINDEX, PLUGIN_REGISTRYNAME); /*stack: window table, event table, error, plugin*/
const struct Plugin* plugin = lua_touserdata(state, -1);
lua_pop(state, 4); /*stack: (empty)*/
_bolt_plugin_stop(plugin->id, plugin->id_length);
} else {
lua_pop(state, 2); /*stack: (empty)*/
}
} else {
lua_pop(state, 3);
}
}
void _bolt_plugin_window_onscroll(struct EmbeddedWindow* window, uint8_t direction) {
lua_State* state = window->plugin;
lua_getfield(state, LUA_REGISTRYINDEX, WINDOWS_REGISTRYNAME); /*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, WINDOW_ONSCROLL); /*stack: window table, event table, event id*/
lua_gettable(state, -2); /*stack: window table, event table, function or nil*/
if (lua_isfunction(state, -1)) {
lua_pushlightuserdata(state, window); /*stack: window table, event table, function, window*/
lua_getfield(state, LUA_REGISTRYINDEX, WINDOW_META_REGISTRYNAME); /*stack: window table, event table, function, window, window metatable*/
lua_setmetatable(state, -2); /*stack: window table, event table, function, window*/
lua_pushboolean(state, direction); /*stack: window table, event table, function, window, direction*/
if (lua_pcall(state, 2, 0, 0)) { /*stack: window table, event table, ?error*/
const char* e = lua_tolstring(state, -1, 0);
printf("plugin window onscroll error: %s\n", e);
lua_getfield(state, LUA_REGISTRYINDEX, PLUGIN_REGISTRYNAME); /*stack: window table, event table, error, plugin*/
const struct Plugin* plugin = lua_touserdata(state, -1);
lua_pop(state, 4); /*stack: (empty)*/
_bolt_plugin_stop(plugin->id, plugin->id_length);
} else {
lua_pop(state, 2); /*stack: (empty)*/
}
} else {
lua_pop(state, 3);
}
}
DEFINE_CALLBACK(mousemotion, MOUSEMOTION, MouseMotionEvent)
DEFINE_CALLBACK(mousebutton, MOUSEBUTTON, MouseButtonEvent)
DEFINE_CALLBACK(scroll, SCROLL, MouseScrollEvent)
DEFINE_WINDOWEVENT(resize, RESIZE, ResizeEvent)
DEFINE_WINDOWEVENT(mousemotion, MOUSEMOTION, MouseMotionEvent)
DEFINE_WINDOWEVENT(mousebutton, MOUSEBUTTON, MouseButtonEvent)
DEFINE_WINDOWEVENT(scroll, SCROLL, MouseScrollEvent)
static int api_apiversion(lua_State* state) {
_bolt_check_argc(state, 0, "apiversion");
@@ -816,11 +869,12 @@ static int api_createwindow(lua_State* state) {
window->id = next_window_id;
window->plugin = state;
_bolt_rwlock_init(&window->lock);
_bolt_rwlock_init(&window->input_lock);
window->metadata.x = lua_tointeger(state, 1);
window->metadata.y = lua_tointeger(state, 2);
window->metadata.width = lua_tointeger(state, 3);
window->metadata.height = lua_tointeger(state, 4);
memset(&window->metadata.input, 0, sizeof(window->metadata.input));
memset(&window->input, 0, sizeof(window->input));
managed_functions.surface_init(&window->surface_functions, window->metadata.width, window->metadata.height, NULL);
lua_getfield(state, LUA_REGISTRYINDEX, WINDOW_META_REGISTRYNAME);
lua_setmetatable(state, -2);
@@ -1264,3 +1318,75 @@ static int api_render3d_worldposition(lua_State* state) {
lua_pushnumber(state, out[2]);
return 3;
}
static int api_resizeevent_size(lua_State* state) {
_bolt_check_argc(state, 1, "resizeevent_size");
struct ResizeEvent* event = lua_touserdata(state, 1);
lua_pushinteger(state, event->width);
lua_pushinteger(state, event->height);
return 2;
}
static int api_mouseevent_xy(lua_State* state) {
_bolt_check_argc(state, 1, "mouseevent_xy");
struct MouseEvent** event = lua_touserdata(state, 1);
lua_pushinteger(state, (*event)->x);
lua_pushinteger(state, (*event)->y);
return 2;
}
static int api_mouseevent_ctrl(lua_State* state) {
_bolt_check_argc(state, 1, "mouseevent_ctrl");
struct MouseEvent** event = lua_touserdata(state, 1);
lua_pushinteger(state, (*event)->ctrl);
return 1;
}
static int api_mouseevent_shift(lua_State* state) {
_bolt_check_argc(state, 1, "mouseevent_shift");
struct MouseEvent** event = lua_touserdata(state, 1);
lua_pushinteger(state, (*event)->shift);
return 1;
}
static int api_mouseevent_meta(lua_State* state) {
_bolt_check_argc(state, 1, "mouseevent_meta");
struct MouseEvent** event = lua_touserdata(state, 1);
lua_pushinteger(state, (*event)->meta);
return 1;
}
static int api_mouseevent_alt(lua_State* state) {
_bolt_check_argc(state, 1, "mouseevent_alt");
struct MouseEvent** event = lua_touserdata(state, 1);
lua_pushinteger(state, (*event)->alt);
return 1;
}
static int api_mouseevent_capslock(lua_State* state) {
_bolt_check_argc(state, 1, "mouseevent_capslock");
struct MouseEvent** event = lua_touserdata(state, 1);
lua_pushinteger(state, (*event)->capslock);
return 1;
}
static int api_mouseevent_numlock(lua_State* state) {
_bolt_check_argc(state, 1, "mouseevent_numlock");
struct MouseEvent** event = lua_touserdata(state, 1);
lua_pushinteger(state, (*event)->numlock);
return 1;
}
static int api_mousebutton_button(lua_State* state) {
_bolt_check_argc(state, 1, "mousebutton_button");
struct MouseButtonEvent* event = lua_touserdata(state, 1);
lua_pushinteger(state, event->button);
return 1;
}
static int api_scroll_direction(lua_State* state) {
_bolt_check_argc(state, 1, "scroll_direction");
struct MouseScrollEvent* event = lua_touserdata(state, 1);
lua_pushinteger(state, event->direction);
return 1;
}

View File

@@ -16,6 +16,21 @@ enum PluginMouseButton {
MBMiddle = 3,
};
// having MouseEvent has the first member of structs allows for generalisation with pointers
struct MouseEvent {
int16_t x;
int16_t y;
uint8_t ctrl;
uint8_t shift;
uint8_t meta;
uint8_t alt;
uint8_t capslock;
uint8_t numlock;
uint8_t mb_left;
uint8_t mb_right;
uint8_t mb_middle;
};
/// Struct containing "vtable" callback information for RenderBatch2D's list of vertices.
/// Unless stated otherwise, functions will be called with three params: the index, the specified
/// userdata, and an output pointer, which must be able to index the returned number of items.
@@ -133,20 +148,17 @@ struct PluginManagedFunctions {
struct WindowPendingInput {
/* bools are listed at the top to make the structure smaller by having less padding in it */
uint8_t mouse_motion;
uint8_t left_click;
uint8_t right_click;
uint8_t middle_click;
uint8_t scroll_up;
uint8_t scroll_down;
int motion_x;
int motion_y;
int left_click_x;
int left_click_y;
int right_click_x;
int right_click_y;
int middle_click_x;
int middle_click_y;
uint8_t mouse_left;
uint8_t mouse_right;
uint8_t mouse_middle;
uint8_t mouse_scroll_down;
uint8_t mouse_scroll_up;
struct MouseEvent mouse_motion_event;
struct MouseEvent mouse_left_event;
struct MouseEvent mouse_right_event;
struct MouseEvent mouse_middle_event;
struct MouseEvent mouse_scroll_down_event;
struct MouseEvent mouse_scroll_up_event;
};
struct EmbeddedWindowMetadata {
@@ -154,7 +166,6 @@ struct EmbeddedWindowMetadata {
int y;
int width;
int height;
struct WindowPendingInput input;
};
struct EmbeddedWindow {
@@ -163,11 +174,14 @@ struct EmbeddedWindow {
struct lua_State* plugin;
RWLock lock; // applies to the metadata
struct EmbeddedWindowMetadata metadata;
RWLock input_lock; // applies to the pending inputs
struct WindowPendingInput input;
};
struct WindowInfo {
RWLock lock; // applies to the whole struct
RWLock lock; // applies to the map
struct hashmap* map;
RWLock input_lock; // applies to the pending inputs
struct WindowPendingInput input;
};
@@ -253,16 +267,4 @@ void _bolt_plugin_handle_3d(struct Render3D*);
/// Sends a RenderMinimap to all plugins.
void _bolt_plugin_handle_minimap(struct RenderMinimapEvent*);
/// Calls the window's handler for resize events.
void _bolt_plugin_window_onresize(struct EmbeddedWindow*, int, int);
/// Calls the window's handler for mouse motion events.
void _bolt_plugin_window_onmousemotion(struct EmbeddedWindow*, int, int);
/// Calls the window's handler for mouse button events.
void _bolt_plugin_window_onmousebutton(struct EmbeddedWindow*, enum PluginMouseButton, int, int);
/// Calls the window's handler for mouse scroll events.
void _bolt_plugin_window_onscroll(struct EmbeddedWindow*, uint8_t);
#endif

View File

@@ -195,6 +195,47 @@ static int api_setcallback3d(lua_State*);
/// image scale (zoom level), and a rough estimate of the tile position it's centered on.
static int api_setcallbackminimap(lua_State*);
/// [-1, +0, -]
/// Sets a callback function for mouse motion events, overwriting the previous callback, if any.
/// Passing a non-function (ideally `nil`) will restore the default setting, which is to have no
/// handler for mouse motion events.
///
/// This callback applies only to inputs received by the game view. If any embedded windows or
/// browsers receive the input, it will be sent to them, and not to this function. note also that
/// this callback will be called at most once per frame: plugins will always receive the latest
/// mouse position, but some position updates will be overwritten by newer ones before the plugin
/// ever receives them.
///
/// The callback will be called with one param, that being a mouse motion object. All of the member
/// functions of that object can be found in this file, prefixed with "api_mouseevent_".
static int setcallbackmousemotion(lua_State*);
/// [-1, +0, -]
/// Sets a callback function for mouse button events, overwriting the previous callback, if any.
/// Passing a non-function (ideally `nil`) will restore the default setting, which is to have no
/// handler for mouse button events.
///
/// This callback applies only to inputs received by the game view. If any embedded windows or
/// browsers receive the input, it will be sent to them, and not to this function.
///
/// The callback will be called with one param, that being a mouse-button object. All of the member
/// functions of that object can be found in this file, prefixed with "api_mouseevent_" and
/// "api_mousebutton_".
static int setcallbackmousebutton(lua_State*);
/// [-1, +0, -]
/// Sets a callback function for mouse scroll events, overwriting the previous callback, if any.
/// Passing a non-function (ideally `nil`) will restore the default setting, which is to have no
/// handler for mouse scroll events.
///
/// This callback applies only to inputs received by the game view. If any embedded windows or
/// browsers receive the input, it will be sent to them, and not to this function.
///
/// The callback will be called with one param, that being a mouse-scroll object. All of the member
/// functions of that object can be found in this file, prefixed with "api_mouseevent_" and
/// "api_scroll_".
static int setcallbackscroll(lua_State*);
/// [-1, +1, -]
/// Returns the number of vertices in a 2D batch object.
static int api_batch2d_vertexcount(lua_State*);
@@ -358,8 +399,9 @@ static int api_window_clear(lua_State*);
/// [-2, +0, -]
/// Sets an event handler for this window for resize events. If the value is a function, it will be
/// called with the following parameters: window, width, height. If the value is not a function, it
/// will not be called, and therefore the plugin will not be notified of resize events.
/// called with two parameters: the window object, and a resize event object. If the value is not a
/// function, it will not be called, and therefore the plugin will not be notified of resize
/// events.
///
/// Resizing a window clears the contents to be transparent, so plugins must redraw the whole
/// window contents in response to a resize event (or any time before it next gets drawn).
@@ -367,25 +409,23 @@ static int api_window_onresize(lua_State*);
/// [-2, +0, -]
/// Sets an event handler for this window for mouse motion events. If the value is a function, it
/// will be called with the following parameters: window, x, y. If the value is not a function, it
/// will not be called, and therefore the plugin will not be notified of mouse motion events. The x
/// and y are pixel coordinates relative to the top-left of the window.
/// will be called with two parameters: the window object, and a mouse motion object. If the value
/// is not a function, it will not be called, and therefore the plugin will not be notified of
/// mouse motion events.
static int api_window_onmousemotion(lua_State*);
/// [-2, +0, -]
/// Sets an event handler for this window for mouse button events. If the value is a function, it
/// will be called with the following parameters: window, button, x, y. If the value is not a
/// function, it will not be called, and therefore the plugin will not be notified of button
/// events. The button may be 1 (left), 2 (right), or 3 (middle). The x and y are pixel coordinates
/// relative to the top-left of the window.
/// will be called with two parameters: the window object, and a mouse-button object. If the value
/// is not a function, it will not be called, and therefore the plugin will not be notified of
/// mouse-button events.
static int api_window_onmousebutton(lua_State*);
/// [-2, +0, -]
/// Sets an event handler for this window for mouse scroll events. If the value is a function, it
/// will be called with the following parameters: window, direction. If the value is not a
/// function, it will not be called, and therefore the plugin will not be notified of mouse scroll
/// events. "direction" is a boolean value: false represents scrolling down, towards the user, and
/// true represents scrolling up, away from the user.
/// will be called with two parameters: the window object, and a mouse-scroll object. If the value
/// is not a function, it will not be called, and therefore the plugin will not be notified of
/// mouse-scroll events.
static int api_window_onscroll(lua_State*);
/// [-1, +1, -]
@@ -479,3 +519,47 @@ static int api_render3d_toscreenspace(lua_State*);
///
/// Equivalent to `render:toworldspace(0, 0, 0)`
static int api_render3d_worldposition(lua_State*);
/// [-1, +2, -]
/// Returns the new width and height that the window was resized to.
static int api_resizeevent_size(lua_State*);
/// [-1, +2, -]
/// Returns the x and y for this mouse event.
static int api_mouseevent_xy(lua_State*);
/// [-1, +1, -]
/// Returns a boolean value indicating whether ctrl was held when this event fired.
static int api_mouseevent_ctrl(lua_State*);
/// [-1, +1, -]
/// Returns a boolean value indicating whether shift was held when this event fired.
static int api_mouseevent_shift(lua_State*);
/// [-1, +1, -]
/// Returns a boolean value indicating whether the meta key (also known as super, command, or the
/// "windows key") was held when this event fired.
static int api_mouseevent_meta(lua_State*);
/// [-1, +1, -]
/// Returns a boolean value indicating whether alt was held when this event fired.
static int api_mouseevent_alt(lua_State*);
/// [-1, +1, -]
/// Returns a boolean value indicating whether caps lock was on when this event fired.
static int api_mouseevent_capslock(lua_State*);
/// [-1, +1, -]
/// Returns a boolean value indicating whether numlock was on when this event fired.
static int api_mouseevent_numlock(lua_State*);
/// [-1, +1, -]
/// Returns an integer representing the mouse button that was pressed. Possible values are 1 for
/// the left mouse button, 2 for the right mouse button, and 3 for the middle mouse button
/// (clicking the mouse wheel).
static int api_mousebutton_button(lua_State*);
/// [-1, +1, -]
/// Returns a boolean value representing the scroll direction. False means scrolling down, toward
/// the user, and true means scrolling up, away from the user.
static int api_scroll_direction(lua_State*);

View File

@@ -403,11 +403,26 @@ static uint8_t _bolt_point_in_rect(int16_t x, int16_t y, int rx, int ry, int rw,
return rx <= x && rx + rw > x && ry <= y && ry + rh > y;
}
static void _bolt_xcb_to_mouse_event(int16_t x, int16_t y, uint16_t state, struct MouseEvent* out) {
// TODO: the rest of this
out->x = x;
out->y = y;
out->ctrl = (state >> 2) & 1;
out->shift = state & 1;
//out->meta;
//out->alt;
//out->capslock;
//out->numlock;
out->mb_left = (state >> 8) & 1;
out->mb_right = (state >> 10) & 1;
out->mb_middle = (state >> 9) & 1;
}
// called by handle_xcb_event
// policy is as follows: if the target window is NOT main_window, do not interfere at all.
// if the target window IS main_window, the event can be swallowed if it's above a window or we're
// currently in a drag action, otherwise the event may also mutate into an enter or leave event.
static uint8_t _bolt_handle_xcb_motion_event(xcb_window_t win, int16_t x, int16_t y, xcb_generic_event_t* e) {
static uint8_t _bolt_handle_xcb_motion_event(xcb_window_t win, int16_t x, int16_t y, uint16_t state, xcb_generic_event_t* e) {
if (win != main_window_xcb) return true;
struct WindowInfo* windows = _bolt_plugin_windowinfo();
uint8_t ret = true;
@@ -423,15 +438,21 @@ static uint8_t _bolt_handle_xcb_motion_event(xcb_window_t win, int16_t x, int16_
_bolt_rwlock_unlock_read(&(*window)->lock);
if (!ret) {
_bolt_rwlock_lock_write(&(*window)->lock);
(*window)->metadata.input.mouse_motion = 1;
(*window)->metadata.input.motion_x = x - (*window)->metadata.x;
(*window)->metadata.input.motion_y = y - (*window)->metadata.y;
_bolt_rwlock_unlock_write(&(*window)->lock);
_bolt_rwlock_lock_write(&(*window)->input_lock);
(*window)->input.mouse_motion = 1;
_bolt_xcb_to_mouse_event(x - (*window)->metadata.x, y - (*window)->metadata.y, state, &(*window)->input.mouse_motion_event);
_bolt_rwlock_unlock_write(&(*window)->input_lock);
break;
}
}
_bolt_rwlock_unlock_read(&windows->lock);
if (ret) {
_bolt_rwlock_lock_write(&windows->input_lock);
windows->input.mouse_motion = 1;
_bolt_xcb_to_mouse_event(x, y, state, &windows->input.mouse_motion_event);
_bolt_rwlock_unlock_write(&windows->input_lock);
}
if (ret != xcb_mousein_fake) {
xcb_mousein_fake = ret;
// mutate the event - note that motion_notify, enter_notify and exit_notify have the exact
@@ -466,7 +487,7 @@ static uint8_t _bolt_handle_xcb_event(xcb_connection_t* c, xcb_generic_event_t*
switch (event->event_type) {
case XCB_INPUT_MOTION: { // when mouse moves (not drag) inside the game window
xcb_input_motion_event_t* event = (xcb_input_motion_event_t*)e;
return _bolt_handle_xcb_motion_event(event->event, event->event_x >> 16, event->event_y >> 16, e);
return _bolt_handle_xcb_motion_event(event->event, event->event_x >> 16, event->event_y >> 16, event->flags, e);
}
case XCB_INPUT_RAW_MOTION: // when mouse moves (not drag) anywhere globally on the PC
case XCB_INPUT_RAW_BUTTON_PRESS: // when pressing a mouse button anywhere globally on the PC
@@ -489,42 +510,69 @@ static uint8_t _bolt_handle_xcb_event(xcb_connection_t* c, xcb_generic_event_t*
while (hashmap_iter(windows->map, &iter, &item)) {
struct EmbeddedWindow** window = item;
_bolt_rwlock_lock_read(&(*window)->lock);
if (_bolt_point_in_rect(event->event_x, event->event_y, (*window)->metadata.x, (*window)->metadata.y, (*window)->metadata.width, (*window)->metadata.height)) {
struct EmbeddedWindowMetadata metadata = (*window)->metadata;
_bolt_rwlock_unlock_read(&(*window)->lock);
if (_bolt_point_in_rect(event->event_x, event->event_y, metadata.x, metadata.y, metadata.width, metadata.height)) {
grabbed_window_id = (*window)->id;
ret = false;
}
_bolt_rwlock_unlock_read(&(*window)->lock);
if (!ret) {
_bolt_rwlock_lock_write(&(*window)->lock);
_bolt_rwlock_lock_write(&(*window)->input_lock);
switch (event->detail) {
case 1:
(*window)->metadata.input.left_click = 1;
(*window)->metadata.input.left_click_x = event->event_x - (*window)->metadata.x;
(*window)->metadata.input.left_click_y = event->event_y - (*window)->metadata.y;
(*window)->input.mouse_left = 1;
_bolt_xcb_to_mouse_event(event->event_x - metadata.x, event->event_y - metadata.y, event->state, &(*window)->input.mouse_left_event);
break;
case 2:
(*window)->metadata.input.middle_click = 1;
(*window)->metadata.input.middle_click_x = event->event_x - (*window)->metadata.x;
(*window)->metadata.input.middle_click_y = event->event_y - (*window)->metadata.y;
(*window)->input.mouse_middle = 1;
_bolt_xcb_to_mouse_event(event->event_x - metadata.x, event->event_y - metadata.y, event->state, &(*window)->input.mouse_middle_event);
break;
case 3:
(*window)->metadata.input.right_click = 1;
(*window)->metadata.input.right_click_x = event->event_x - (*window)->metadata.x;
(*window)->metadata.input.right_click_y = event->event_y - (*window)->metadata.y;
(*window)->input.mouse_right = 1;
_bolt_xcb_to_mouse_event(event->event_x - metadata.x, event->event_y - metadata.y, event->state, &(*window)->input.mouse_right_event);
break;
case 4:
(*window)->metadata.input.scroll_up = 1;
(*window)->input.mouse_scroll_up = 1;
_bolt_xcb_to_mouse_event(event->event_x - metadata.x, event->event_y - metadata.y, event->state, &(*window)->input.mouse_scroll_up_event);
break;
case 5:
(*window)->metadata.input.scroll_down = 1;
(*window)->input.mouse_scroll_down = 1;
_bolt_xcb_to_mouse_event(event->event_x - metadata.x, event->event_y - metadata.y, event->state, &(*window)->input.mouse_scroll_down_event);
break;
}
_bolt_rwlock_unlock_write(&(*window)->lock);
_bolt_rwlock_unlock_write(&(*window)->input_lock);
break;
}
}
_bolt_rwlock_unlock_read(&windows->lock);
if (ret) {
_bolt_rwlock_lock_write(&windows->input_lock);
switch (event->detail) {
case 1:
windows->input.mouse_left = 1;
_bolt_xcb_to_mouse_event(event->event_x, event->event_y, event->state, &windows->input.mouse_left_event);
break;
case 2:
windows->input.mouse_middle = 1;
_bolt_xcb_to_mouse_event(event->event_x, event->event_y, event->state, &windows->input.mouse_middle_event);
break;
case 3:
windows->input.mouse_right = 1;
_bolt_xcb_to_mouse_event(event->event_x, event->event_y, event->state, &windows->input.mouse_right_event);
break;
case 4:
windows->input.mouse_scroll_up = 1;
_bolt_xcb_to_mouse_event(event->event_x, event->event_y, event->state, &windows->input.mouse_scroll_up_event);
break;
case 5:
windows->input.mouse_scroll_down = 1;
_bolt_xcb_to_mouse_event(event->event_x, event->event_y, event->state, &windows->input.mouse_scroll_down_event);
break;
}
_bolt_rwlock_unlock_write(&windows->input_lock);
}
return ret;
}
case XCB_BUTTON_RELEASE: { // when releasing a mouse button, for which the press was received by the game window