From 795b1a96eb648098dba8b829125542874012d32d Mon Sep 17 00:00:00 2001 From: Addison Schuhardt Date: Mon, 29 Nov 2021 00:11:28 -0800 Subject: [PATCH] cleaning-up mouse events and exposing mouse input and new window methods to the Lua API --- include/color.h | 2 +- include/mouse.h | 9 --- include/procyon.h | 1 + include/state.h | 5 +- lua/CMakeLists.txt | 5 ++ lua/README.md | 15 +++-- lua/sample/mouse.lua | 23 +++++++ lua/src/script/input.c | 142 +++++++++++++++++++++++++++++++++++----- lua/src/script/window.c | 24 +++++++ src/keys.c | 4 -- src/mouse.c | 18 ----- src/window.c | 8 ++- 12 files changed, 198 insertions(+), 58 deletions(-) create mode 100644 lua/sample/mouse.lua diff --git a/include/color.h b/include/color.h index 581e4cc..baa9805 100644 --- a/include/color.h +++ b/include/color.h @@ -3,7 +3,7 @@ #include -#define COLOR_TO_INT(r, g, b) (b | g << 8 | r << 16) +#define COLOR_TO_INT(r, g, b) ((b) | (g) << 8 | (r) << 16) typedef struct procy_color_t { int value; diff --git a/include/mouse.h b/include/mouse.h index de01d76..a11f644 100644 --- a/include/mouse.h +++ b/include/mouse.h @@ -16,15 +16,6 @@ typedef enum procy_mouse_button_t { MOUSE_BUTTON_MIDDLE = MOUSE_BUTTON_3 } procy_mouse_button_t; -typedef enum procy_mouse_mod_t { - MOUSE_MOD_NONE = 0, - MOUSE_MOD_SHIFT = 1, - MOUSE_MOD_CTRL = 1 << 1, - MOUSE_MOD_ALT = 1 << 2 -} procy_mouse_mod_t; - procy_mouse_button_t procy_map_glfw_mouse_button(int button); -procy_mouse_mod_t procy_map_glfw_mouse_modifier(int mod); - #endif diff --git a/include/procyon.h b/include/procyon.h index eff6dcf..5e3b800 100644 --- a/include/procyon.h +++ b/include/procyon.h @@ -19,6 +19,7 @@ #include "color.h" #include "drawing.h" #include "keys.h" +#include "mouse.h" #include "state.h" #include "window.h" diff --git a/include/state.h b/include/state.h index 3d2b75b..f6ffebc 100644 --- a/include/state.h +++ b/include/state.h @@ -5,7 +5,6 @@ struct procy_state_t; struct procy_key_info_t; -enum procy_mouse_mod_t; enum procy_mouse_button_t; typedef void (*procy_on_load_callback_t)(struct procy_state_t *); @@ -24,10 +23,10 @@ typedef void (*procy_on_mouse_moved_callback_t)(struct procy_state_t *, double, double); typedef void (*procy_on_mouse_pressed_callback_t)(struct procy_state_t *, enum procy_mouse_button_t, - enum procy_mouse_mod_t); + bool, bool, bool); typedef void (*procy_on_mouse_released_callback_t)(struct procy_state_t *, enum procy_mouse_button_t, - enum procy_mouse_mod_t); + bool, bool, bool); typedef struct procy_state_t { void *data; diff --git a/lua/CMakeLists.txt b/lua/CMakeLists.txt index 94c038f..31b86bd 100644 --- a/lua/CMakeLists.txt +++ b/lua/CMakeLists.txt @@ -36,3 +36,8 @@ add_custom_target(script_test COMMAND procyon-lua --debug -e "${CMAKE_CURRENT_SOURCE_DIR}/sample/test.lua" DEPENDS procyon-lua copy_spritesheet_for_script_test USES_TERMINAL) + +add_custom_target(mouse_test + COMMAND procyon-lua --debug -e "${CMAKE_CURRENT_SOURCE_DIR}/sample/mouse.lua" + DEPENDS procyon-lua + USES_TERMINAL) diff --git a/lua/README.md b/lua/README.md index 7f97fdd..72da005 100644 --- a/lua/README.md +++ b/lua/README.md @@ -47,6 +47,8 @@ High-level objects are grouped into global tables that I refer to as "modules". - `window.reload()` - Returns nothing. Causes the window and script environment to be disposed-of and reloaded (functionally "restarts" the app). - `window.set_color(color)` - Returns nothing. Sets the "clear" color used for the window background. - `window.set_high_fps(enabled)` - Returns nothing. Enables or enables "high-fps mode", in which the window will attempt to update with a frequency that matches the display refresh rate. Otherwise, when high-fps mode is disabled, the window only updates every full second or when input events are triggered. +- `window.set_fullscreen()` - Returns nothing. Switches from windowed mode to fullscreen. +- `window.set_windowed()` - Returns nothing. Switches from fullscreen mode to windowed. #### Fields - `window.on_draw` - If assigned, `on_draw` is called before each new frame is drawn. Perform any drawing routines here. @@ -77,12 +79,11 @@ High-level objects are grouped into global tables that I refer to as "modules". #### Fields - `input.on_key_pressed` - If assigned, `on_key_pressed` is called when a key is pressed. It is passed a single argument, a table with the following fields: - `value` - An integer indicating the scancode of the key that was entered. Comparable to the `KEY_...` global values. - - `name` - A string indicating the name of the key that was pressed. The same name is used as the names of the `KEY_...` global values. - - `ctrl`, `alt`, `shift` - Boolean flags indicating whether one of these modifier was pressed at the time the event was raised. + - `ctrl`, `alt`, `shift` - Boolean flags indicating whether one of these modifiers was pressed at the time the event was raised. ```lua input.on_key_pressed = function(key) if key.value == KEY_ESCAPE then - log.info(string.format('%s was pressed!', key.name)) + log.info(string.format('Goodbye!')) window.close() end end @@ -91,7 +92,11 @@ end - `input.on_key_released` - Same as `on_key_pressed` above, but called when a key is released. - `input.on_char_entered` - If assigned, `on_char_entered` is called when the user enters text via the keyboard. - `KEY_A ...` - Named key objects. See `script/keys.h` for a complete list. -- `keys[k].name`, `keys[k].value` - In addition to named objects, data pertaining to each key is stored in the `keys` table indexed by the key values themselves. +- `input.on_mouse_pressed` - If assigned, `on_mouse_pressed is called when a mouse button is pressed. It is passed a single argument, a table with the following fields: + - `value` - An integer representing the button that was pressed. One of `MOUSE_LEFT`, `MOUSE_RIGHT`, `MOUSE_MIDDLE`, or `MOUSE_[0,8]`. + - `ctrl`, `alt`, `shift` - Boolean flags indicating whether one of these modifiers was pressed at the time the event was raised. +- `input.on_mouse_released` - Same as `on_mouse_pressed` above, but called when the button is released. +- `input.on_mouse_moved` - If assigned, `on_mouse_moved` is called when the mouse has moved. It is passed two floating-point arguments, `x` and `y`, which represent the new position in screen coordinates of the mouse cursor. --- @@ -109,7 +114,7 @@ Continuous noise is useful for all kinds of things. ### Plane -A plane is a 2D bitmap data structure. Elements in the plane consist of 8-bit integers, and are accessed by an (X, Y) index. Planes have a fixed width and height which are exposed to the developer. +A plane is a 2D bitmap data structure. Elements in the plane consist of byte values, and are accessed by an (X, Y) index. Planes have a fixed width and height which are exposed to the developer. #### Functions - `plane.from(w, h, value)` - Returns a new plane object with dimensions `w` and `h`, having each of its elements initialized to `value`. The dimensions are made accessible via the `width` and `height` fields in the resulting table. diff --git a/lua/sample/mouse.lua b/lua/sample/mouse.lua new file mode 100644 index 0000000..1480695 --- /dev/null +++ b/lua/sample/mouse.lua @@ -0,0 +1,23 @@ +position = { x = 0, y = 0 } + +window.on_draw = function() + draw.string(100, 100, string.format("Position: (%d, %d)", position.x, position.y)) + draw.string(100, 200, "Click to change colors") +end + +input.on_mouse_moved = function(x, y) + position.x = math.floor(x) + position.y = math.floor(y) +end + +input.on_mouse_released = function(button) + if button.value == MOUSE_LEFT then + window.set_color(color.from_rgb(0.8, 0.3, 0.3)) + else + window.set_color(color.from_rgb(0.1, 0.4, 0.8)) + end +end + +input.on_mouse_pressed = function(button) + window.set_color(color.from_rgb(0.4, 0.6, 0.5)) +end diff --git a/lua/src/script/input.c b/lua/src/script/input.c index c5615ae..ebf6c6a 100644 --- a/lua/src/script/input.c +++ b/lua/src/script/input.c @@ -10,13 +10,20 @@ #define FUNC_EVENTS_KEYPRESS "on_key_pressed" #define FUNC_EVENTS_KEYRELEASE "on_key_released" #define FUNC_EVENTS_CHAR "on_char_entered" +#define FUNC_EVENTS_MOUSE_MOVED "on_mouse_moved" +#define FUNC_EVENTS_MOUSE_PRESS "on_mouse_pressed" +#define FUNC_EVENTS_MOUSE_RELEASE "on_mouse_released" #define FIELD_KEY_VALUE "value" -#define FIELD_KEY_NAME "name" #define FIELD_KEY_CTRL "ctrl" #define FIELD_KEY_SHIFT "shift" #define FIELD_KEY_ALT "alt" +#define FIELD_MOUSE_VALUE "value" +#define FIELD_MOUSE_CTRL "ctrl" +#define FIELD_MOUSE_SHIFT "shift" +#define FIELD_MOUSE_ALT "alt" + #define CHAR_MAX_CODEPOINT 255 static void push_key_arg(lua_State *L, procy_key_info_t *key, bool shift, @@ -26,9 +33,6 @@ static void push_key_arg(lua_State *L, procy_key_info_t *key, bool shift, lua_pushinteger(L, key->value); lua_setfield(L, -2, FIELD_KEY_VALUE); - lua_pushstring(L, key->name); - lua_setfield(L, -2, FIELD_KEY_NAME); - lua_pushboolean(L, shift); lua_setfield(L, -2, FIELD_KEY_SHIFT); @@ -39,8 +43,25 @@ static void push_key_arg(lua_State *L, procy_key_info_t *key, bool shift, lua_setfield(L, -2, FIELD_KEY_ALT); } -static void handle_key_pressed(procy_state_t *const state, procy_key_info_t key, - bool shift, bool ctrl, bool alt) { +static void push_mouse_button_arg(lua_State *L, procy_mouse_button_t button, + bool shift, bool ctrl, bool alt) { + lua_newtable(L); + + lua_pushinteger(L, button); + lua_setfield(L, -2, FIELD_MOUSE_VALUE); + + lua_pushboolean(L, shift); + lua_setfield(L, -2, FIELD_MOUSE_SHIFT); + + lua_pushboolean(L, ctrl); + lua_setfield(L, -2, FIELD_MOUSE_CTRL); + + lua_pushboolean(L, alt); + lua_setfield(L, -2, FIELD_MOUSE_ALT); +} + +static void key_pressed(procy_state_t *state, procy_key_info_t key, bool shift, + bool ctrl, bool alt) { lua_State *L = ((script_env_t *)state->data)->L; lua_getglobal(L, TBL_INPUT); @@ -56,9 +77,8 @@ static void handle_key_pressed(procy_state_t *const state, procy_key_info_t key, lua_pop(L, lua_gettop(L)); } -static void handle_key_released(procy_state_t *const state, - procy_key_info_t key, bool shift, bool ctrl, - bool alt) { +static void key_released(procy_state_t *state, procy_key_info_t key, bool shift, + bool ctrl, bool alt) { lua_State *L = ((script_env_t *)state->data)->L; lua_getglobal(L, TBL_INPUT); @@ -67,15 +87,14 @@ static void handle_key_released(procy_state_t *const state, push_key_arg(L, &key, shift, ctrl, alt); if (lua_pcall(L, 1, 0, 0) == LUA_ERRRUN) { LOG_SCRIPT_ERROR(L, "Error calling %s.%s: %s", TBL_INPUT, - FUNC_EVENTS_KEYPRESS, lua_tostring(L, -1)); + FUNC_EVENTS_KEYRELEASE, lua_tostring(L, -1)); } } lua_pop(L, lua_gettop(L)); } -static void handle_char_entered(procy_state_t *const state, - unsigned int codepoint) { +static void char_entered(procy_state_t *state, unsigned int codepoint) { // for the time being we're using a font that only supports so-called // "extended ASCII", up to value 255 if (codepoint > CHAR_MAX_CODEPOINT) { @@ -100,6 +119,57 @@ static void handle_char_entered(procy_state_t *const state, lua_pop(L, lua_gettop(L)); } +static void mouse_moved(procy_state_t *state, double x, double y) { + lua_State *L = ((script_env_t *)state->data)->L; + + lua_getglobal(L, TBL_INPUT); + lua_getfield(L, -1, FUNC_EVENTS_MOUSE_MOVED); + if (lua_isfunction(L, -1)) { + lua_pushnumber(L, x); + lua_pushnumber(L, y); + if (lua_pcall(L, 2, 0, 0) == LUA_ERRRUN) { + LOG_SCRIPT_ERROR(L, "Error calling %s.%s: %s", TBL_INPUT, + FUNC_EVENTS_MOUSE_MOVED, lua_tostring(L, -1)); + } + } + + lua_pop(L, lua_gettop(L)); +} + +static void mouse_released(procy_state_t *state, procy_mouse_button_t button, + bool shift, bool ctrl, bool alt) { + lua_State *L = ((script_env_t *)state->data)->L; + + lua_getglobal(L, TBL_INPUT); + lua_getfield(L, -1, FUNC_EVENTS_MOUSE_RELEASE); + if (lua_isfunction(L, -1)) { + push_mouse_button_arg(L, button, shift, ctrl, alt); + if (lua_pcall(L, 1, 0, 0) == LUA_ERRRUN) { + LOG_SCRIPT_ERROR(L, "Error calling %s.%s: %s", TBL_INPUT, + FUNC_EVENTS_MOUSE_RELEASE, lua_tostring(L, -1)); + } + } + + lua_pop(L, lua_gettop(L)); +} + +static void mouse_pressed(procy_state_t *state, procy_mouse_button_t button, + bool shift, bool ctrl, bool alt) { + lua_State *L = ((script_env_t *)state->data)->L; + + lua_getglobal(L, TBL_INPUT); + lua_getfield(L, -1, FUNC_EVENTS_MOUSE_PRESS); + if (lua_isfunction(L, -1)) { + push_mouse_button_arg(L, button, shift, ctrl, alt); + if (lua_pcall(L, 1, 0, 0) == LUA_ERRRUN) { + LOG_SCRIPT_ERROR(L, "Error calling %s.%s: %s", TBL_INPUT, + FUNC_EVENTS_MOUSE_PRESS, lua_tostring(L, -1)); + } + } + + lua_pop(L, lua_gettop(L)); +} + static void add_event_handler_table(lua_State *L) { lua_newtable(L); lua_setglobal(L, TBL_INPUT); @@ -121,10 +191,52 @@ static void add_keys(lua_State *L) { free(keys); } +static void add_mouse_values(lua_State *L) { + lua_pushinteger(L, MOUSE_BUTTON_0); + lua_setglobal(L, "MOUSE_0"); + + lua_pushinteger(L, MOUSE_BUTTON_1); + lua_setglobal(L, "MOUSE_1"); + + lua_pushinteger(L, MOUSE_BUTTON_2); + lua_setglobal(L, "MOUSE_2"); + + lua_pushinteger(L, MOUSE_BUTTON_3); + lua_setglobal(L, "MOUSE_3"); + + lua_pushinteger(L, MOUSE_BUTTON_4); + lua_setglobal(L, "MOUSE_4"); + + lua_pushinteger(L, MOUSE_BUTTON_5); + lua_setglobal(L, "MOUSE_5"); + + lua_pushinteger(L, MOUSE_BUTTON_6); + lua_setglobal(L, "MOUSE_6"); + + lua_pushinteger(L, MOUSE_BUTTON_7); + lua_setglobal(L, "MOUSE_7"); + + lua_pushinteger(L, MOUSE_BUTTON_8); + lua_setglobal(L, "MOUSE_8"); + + lua_pushinteger(L, MOUSE_BUTTON_LEFT); + lua_setglobal(L, "MOUSE_LEFT"); + + lua_pushinteger(L, MOUSE_BUTTON_RIGHT); + lua_setglobal(L, "MOUSE_RIGHT"); + + lua_pushinteger(L, MOUSE_BUTTON_MIDDLE); + lua_setglobal(L, "MOUSE_MIDDLE"); +} + void add_input(lua_State *L, script_env_t *env) { add_event_handler_table(L); add_keys(L); - env->state->on_key_pressed = handle_key_pressed; - env->state->on_key_released = handle_key_released; - env->state->on_char_entered = handle_char_entered; + add_mouse_values(L); + env->state->on_key_pressed = key_pressed; + env->state->on_key_released = key_released; + env->state->on_char_entered = char_entered; + env->state->on_mouse_moved = mouse_moved; + env->state->on_mouse_pressed = mouse_pressed; + env->state->on_mouse_released = mouse_released; } diff --git a/lua/src/script/window.c b/lua/src/script/window.c index 80013be..ce53097 100644 --- a/lua/src/script/window.c +++ b/lua/src/script/window.c @@ -1,3 +1,5 @@ +#include "window.h" + #include #include #include @@ -22,6 +24,8 @@ #define FUNC_SET_SCALE "set_scale" #define FUNC_GET_SCALE "get_scale" #define FUNC_RESET_SCALE "reset_scale" +#define FUNC_SET_FULLSCREEN "set_fullscreen" +#define FUNC_SET_WINDOWED "set_windowed" static int close_window(lua_State *L) { lua_getfield(L, LUA_REGISTRYINDEX, GLOBAL_WINDOW_PTR); @@ -216,6 +220,24 @@ static int reset_window_scale(lua_State *L) { return 0; } +static int set_window_fullscreen(lua_State *L) { + lua_getfield(L, LUA_REGISTRYINDEX, GLOBAL_WINDOW_PTR); + procy_window_t *window = (procy_window_t *)lua_touserdata(L, -1); + + procy_set_fullscreen(window); + + return 0; +} + +static int set_window_windowed(lua_State *L) { + lua_getfield(L, LUA_REGISTRYINDEX, GLOBAL_WINDOW_PTR); + procy_window_t *window = (procy_window_t *)lua_touserdata(L, -1); + + procy_set_windowed(window); + + return 0; +} + void add_window(lua_State *L, script_env_t *env) { env->state->on_draw = perform_draw; env->state->on_resize = handle_window_resized; @@ -233,6 +255,8 @@ void add_window(lua_State *L, script_env_t *env) { {FUNC_GET_SCALE, get_window_scale}, {FUNC_RESET_SCALE, reset_window_scale}, {FUNC_SET_TITLE, set_window_title}, + {FUNC_SET_FULLSCREEN, set_window_fullscreen}, + {FUNC_SET_WINDOWED, set_window_windowed}, {NULL, NULL}}; luaL_newlib(L, methods); lua_setglobal(L, TBL_WINDOW); diff --git a/src/keys.c b/src/keys.c index 2c52393..273fe25 100644 --- a/src/keys.c +++ b/src/keys.c @@ -126,10 +126,6 @@ const key_info_t KEYS[] = {{"KEY_SPACE", KEY_SPACE}, {"KEY_RIGHT_SUPER", KEY_RIGHT_SUPER}, {"KEY_MENU", KEY_MENU}}; -/* --------------------------- */ -/* Public interface definition */ -/* --------------------------- */ - void procy_get_keys(key_info_t **buffer, size_t *len) { *len = sizeof(KEYS) / sizeof(key_info_t); *buffer = malloc(*len * sizeof(key_info_t)); diff --git a/src/mouse.c b/src/mouse.c index e24f786..be8e4c9 100644 --- a/src/mouse.c +++ b/src/mouse.c @@ -24,21 +24,3 @@ procy_mouse_button_t procy_map_glfw_mouse_button(int button) { return MOUSE_BUTTON_0; } } - -procy_mouse_mod_t procy_map_glfw_mouse_modifier(int mod) { - procy_mouse_mod_t result = MOUSE_MOD_NONE; - - if ((mod & GLFW_MOD_SHIFT) == GLFW_MOD_SHIFT) { - result |= MOUSE_MOD_SHIFT; - } - - if ((mod & GLFW_MOD_CONTROL) == GLFW_MOD_CONTROL) { - result |= MOUSE_MOD_CTRL; - } - - if ((mod & GLFW_MOD_ALT) == GLFW_MOD_ALT) { - result |= MOUSE_MOD_ALT; - } - - return result; -} diff --git a/src/window.c b/src/window.c index 7701ebf..bce69bb 100644 --- a/src/window.c +++ b/src/window.c @@ -117,11 +117,13 @@ static void mouse_action(GLFWwindow *w, int button, int action, int mods) { window_t *window = (window_t *)glfwGetWindowUserPointer(w); state_t *state = window->state; procy_mouse_button_t mapped_button = procy_map_glfw_mouse_button(button); - procy_mouse_mod_t mapped_mods = procy_map_glfw_mouse_modifier(mods); + bool shift = (mods & GLFW_MOD_SHIFT) == GLFW_MOD_SHIFT; + bool ctrl = (mods & GLFW_MOD_CONTROL) == GLFW_MOD_CONTROL; + bool alt = (mods & GLFW_MOD_ALT) == GLFW_MOD_ALT; if (action == GLFW_PRESS && state->on_mouse_pressed != NULL) { - state->on_mouse_pressed(state, mapped_button, mapped_mods); + state->on_mouse_pressed(state, mapped_button, shift, ctrl, alt); } else if (action == GLFW_RELEASE && state->on_mouse_released != NULL) { - state->on_mouse_released(state, mapped_button, mapped_mods); + state->on_mouse_released(state, mapped_button, shift, ctrl, alt); } }