diff --git a/src/library/gl.c b/src/library/gl.c index 868945c..5ace69f 100644 --- a/src/library/gl.c +++ b/src/library/gl.c @@ -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*)) { diff --git a/src/library/plugin/plugin.c b/src/library/plugin/plugin.c index 119bb0c..9768f01 100644 --- a/src/library/plugin/plugin.c +++ b/src/library/plugin/plugin.c @@ -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; +} diff --git a/src/library/plugin/plugin.h b/src/library/plugin/plugin.h index 5cb04d2..3a36bdd 100644 --- a/src/library/plugin/plugin.h +++ b/src/library/plugin/plugin.h @@ -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 diff --git a/src/library/plugin/plugin_api.h b/src/library/plugin/plugin_api.h index ce1836f..54277b2 100644 --- a/src/library/plugin/plugin_api.h +++ b/src/library/plugin/plugin_api.h @@ -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*); diff --git a/src/library/so/main.c b/src/library/so/main.c index e9b2ec5..c4d5a62 100644 --- a/src/library/so/main.c +++ b/src/library/so/main.c @@ -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