diff options
author | sanine <sanine.not@pm.me> | 2022-03-01 22:42:10 -0600 |
---|---|---|
committer | sanine <sanine.not@pm.me> | 2022-03-01 22:42:10 -0600 |
commit | 10288765588673645c1cc0a6e3d2245aff3f9080 (patch) | |
tree | 3a56953535c3e26156b41331631d51e25ff6bdb9 | |
parent | 9c238237597de90c73cc65c3fccf2f49bfaa46b4 (diff) |
add basic window functions, option parsing, and main loop
-rw-r--r-- | CMakeLists.txt | 3 | ||||
-rw-r--r-- | src/gl/honey_gl.c | 193 | ||||
-rw-r--r-- | src/gl/honey_gl.h | 15 | ||||
-rw-r--r-- | src/main.c | 132 | ||||
-rw-r--r-- | src/options/honey_options.c | 55 | ||||
-rw-r--r-- | src/options/honey_options.h | 14 |
6 files changed, 398 insertions, 14 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 3dec1d7..4e9bf22 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,7 @@ add_library(glad ${SRC_ROOT}/gl/glad/glad.c) set(HONEY_LIB_FILES ${SRC_ROOT}/logging/logging.c ${SRC_ROOT}/gl/honey_gl.c + ${SRC_ROOT}/options/honey_options.c ) set(SOURCE_FILES @@ -28,7 +29,7 @@ set(SOURCE_FILES add_executable(honey ${SOURCE_FILES}) -set(LIBRARIES ${LUA_LIBRARIES} assimp glad cairo stb_image m) +set(LIBRARIES ${LUA_LIBRARIES} honeysuckle glad cairo m) if (WIN32) set(LIBRARIES ${LIBRARIES} glfw3 opengl32) else() diff --git a/src/gl/honey_gl.c b/src/gl/honey_gl.c index bbb775f..6a113d1 100644 --- a/src/gl/honey_gl.c +++ b/src/gl/honey_gl.c @@ -1,10 +1,197 @@ #include <honeysuckle.h> + +#include <stdbool.h> + #include "logging/logging.h" #include "gl/honey_gl.h" -int honey_gl_window_ref = LUA_NOREF; -bool honey_gl_setup() +struct honey_window window; + +static int resize_callback_ref = LUA_NOREF; +static int resize_data_ref = LUA_NOREF; + +static int focus_callback_ref = LUA_NOREF; +static int focus_data_ref = LUA_NOREF; + +static void error_callback(int code, const char *description); +static void resize_callback(GLFWwindow *w, int width, int height); +static void focus_callback(GLFWwindow *w, int focus); + +static int create_window(lua_State *L); + +static int window_get_size(lua_State *L); +static int window_set_size(lua_State *L); +static int window_set_resize_callback(lua_State *L); +static int window_unset_resize_callback(lua_State *L); + + +void setup_window(lua_State *L, int tbl_index) { - return false; + /* initialize window struct */ + window.created = false; + window.L = L; + + /* set error handler */ + glfwSetErrorCallback(error_callback); + + /* create window table */ + hs_create_table + (L, + hs_str_cfunc("createWindow", create_window), + hs_str_cfunc("getSize", window_get_size), + hs_str_cfunc("setSize", window_set_size), + hs_str_cfunc("setResizeCallback", window_set_resize_callback), + hs_str_cfunc("unsetResizeCallback", window_unset_resize_callback) + ); + + /* set honey table entry */ + lua_setfield(L, tbl_index, "window"); +} + + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * glfw callbacks + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +static void error_callback(int code, const char *description) +{ + honey_error("GLFW error %d: %s", code, description); + hs_throw_error(window.L, "(GLFW error %d) %s", code, description); +} + + +static void resize_callback(GLFWwindow *w, int width, int height) +{ + lua_State *L = window.L; + + if (resize_callback_ref == LUA_NOREF) + /* no resize callback set */ + return; + + /* push callback function */ + hs_rload(L, resize_callback_ref); + + lua_pushinteger(L, width); + lua_pushinteger(L, height); + + if (resize_data_ref == LUA_NOREF || resize_data_ref == LUA_REFNIL) + lua_pushnil(L); + else + hs_rload(L, resize_data_ref); + + hs_call(L, 3, 0); +} + + +static void focus_callback(GLFWwindow *w, int focus) +{ + lua_State *L = window.L; + + if (focus_callback_ref == LUA_NOREF) + /* no focus callback set */ + return; + + /* push callback function */ + hs_rload(L, focus_callback_ref); + + lua_pushboolean(L, focus); + + if (focus_data_ref == LUA_NOREF || focus_data_ref == LUA_REFNIL) + lua_pushnil(L); + else + hs_rload(L, focus_data_ref); + + hs_call(L, 2, 0); +} + + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * window + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +static int create_window(lua_State *L) +{ + /* return immediately if already called */ + if (window.created) return 0; + + lua_Integer x, y; + hs_parse_args(L, hs_int(x), hs_int(y)); + + if (!glfwInit()) { + /* window creation failed :c */ + honey_fatal("failed to create window!"); + hs_throw_error(L, "failed to create window!"); + } + + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + + window.window = glfwCreateWindow(x, y, "honey", NULL, NULL); + glfwMakeContextCurrent(window.window); + + if (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress)) { + honey_fatal("failed to initialize GLAD"); + glfwTerminate(); + hs_throw_error(L, "failed to initialize GLAD"); + } + + /* bind resize/focus callbacks */ + glfwSetWindowSizeCallback(window.window, resize_callback); + glfwSetWindowFocusCallback(window.window, focus_callback); +} + + +static int window_get_size(lua_State *L) +{ + int width, height; + glfwGetWindowSize(window.window, &width, &height); + lua_pushinteger(L, width); + lua_pushinteger(L, height); + return 2; +} + + +static int window_set_size(lua_State *L) +{ + lua_Integer width, height; + hs_parse_args(L, hs_int(width), hs_int(height)); + glfwSetWindowSize(window.window, width, height); + return 0; +} + + +static int window_set_resize_callback(lua_State *L) +{ + int cb, data; + hs_parse_args(L, hs_func(cb), hs_any(data)); + + lua_pushvalue(L, cb); + resize_callback_ref = hs_rstore(L); + + lua_pushvalue(L, data); + resize_data_ref = hs_rstore(L); + + return 0; +} + + +static int window_unset_resize_callback(lua_State *L) +{ + if (resize_callback_ref != LUA_NOREF) + hs_rdel(L, resize_callback_ref); + if (resize_data_ref != LUA_NOREF) + hs_rdel(L, resize_callback_ref); + + resize_callback_ref = LUA_NOREF; + resize_data_ref = LUA_NOREF; + + return 0; } diff --git a/src/gl/honey_gl.h b/src/gl/honey_gl.h index fc57037..8d5cdd9 100644 --- a/src/gl/honey_gl.h +++ b/src/gl/honey_gl.h @@ -1,18 +1,17 @@ #ifndef HONEY_GL_H #define HONEY_GL_H -#include <stdbool.h> - -#ifdef UNIT_TEST -#include "test/mock/mock_GLFW.h" -#else #include "glad/glad.h" #include <GLFW/glfw3.h> -#endif -extern int honey_gl_window_ref; +struct honey_window { + GLFWwindow * window; + bool created; + lua_State *L; +}; -bool honey_gl_setup(); +extern struct honey_window window; +void setup_window(lua_State *L, int tbl_index); #endif @@ -1,7 +1,135 @@ #include <stdio.h> +#include <honeysuckle.h> -int main() +#include "logging/logging.h" +#include "options/honey_options.h" +#include "gl/honey_gl.h" + +static int get_func(lua_State *L, char *name); +static void loop(lua_State *L, int update, int draw); + + +int main(int argc, char **argv) { - printf("hello, world!\n"); + /* process command-line options */ + struct honey_options options; + if (!parse_options(&options, argc, argv)) + return 1; + if (options.display_help) + /* help displayed, exit */ + return 0; + + honey_info("options:\n" + " main script: %s\n" + " log level: %s\n", + options.main_script, + honey_log_level_str(options.log_level)); + + /* setup lua environment */ + honey_debug("initialize lua_State\n"); + lua_State *L = luaL_newstate(); + luaL_openlibs(L); + + honey_debug("load honey bindings\n"); + lua_createtable(L, 0, 1); + int index = lua_gettop(L); + + setup_window(L, index); + + lua_setglobal(L, "honey"); + + /* load main script */ + honey_debug("loading '%s'\n", options.main_script); + + if (luaL_loadfile(L, options.main_script)) { + const char *error = lua_tostring(L, -1); + honey_fatal("%s\n", error); + return 1; + } + + if (!hs_call(L, 0, 0)) { + const char *error = lua_tostring(L, -1); + honey_fatal("unhandled error: %s", error); + goto close; /* sorry */ + } + + if (!window.created) { + lua_getglobal(L, "honey"); + lua_getfield(L, -1, "window"); + lua_getfield(L, -1, "createWindow"); + lua_pushinteger(L, 640); + lua_pushinteger(L, 480); + hs_call(L, 2, 0); + } + + int update = get_func(L, "update"); + if (update) honey_info("honey.update: %s\n", lua_tostring(L, update)); + else honey_info("honey.update: (nil)\n"); + int draw = get_func(L, "draw"); + if (draw) honey_info("honey.draw: %s\n", lua_tostring(L, draw)); + else honey_info("honey.draw: (nil)\n"); + + while(!glfwWindowShouldClose(window.window)) + loop(L, update, draw); + + close: + lua_close(L); + glfwTerminate(); + return 0; } + + +static int get_func(lua_State *L, char *name) +{ + lua_getglobal(L, "honey"); + int index = lua_gettop(L); + lua_getfield(L, -1, name); + + if (lua_isfunction(L, -1)) { + lua_remove(L, index); + return lua_gettop(L); + } + else { + lua_pop(L, 2); + return 0; + } +} + + +static void loop(lua_State *L, int update, int draw) +{ + static float prev_time = 0; + static float draw_time = 0; + + float current_time = (float) glfwGetTime(); + float dt = current_time - prev_time; + prev_time = current_time; + + draw_time += dt; + glfwPollEvents(); + + if (update) { + lua_pushvalue(L, update); + lua_pushnumber(L, dt); + if (!hs_call(L, 1, 0)) { + /* error! */ + const char *error = lua_tostring(L, -1); + honey_fatal("unhandled error: %s\n", error); + glfwSetWindowShouldClose(window.window, 1); + } + } + + /* cap framerate at 60fps */ + if (draw && draw_time > 0.016) { + draw_time -= 0.016; + lua_pushvalue(L, draw); + if(!hs_call(L, 0, 0)) { + /* error! */ + const char *error = lua_tostring(L, -1); + honey_fatal("unhandled error: %s\n", error); + glfwSetWindowShouldClose(window.window, 1); + } + } +} + diff --git a/src/options/honey_options.c b/src/options/honey_options.c new file mode 100644 index 0000000..cf93588 --- /dev/null +++ b/src/options/honey_options.c @@ -0,0 +1,55 @@ +#include <stdio.h> +#include <unistd.h> /* todo: create windows-compatible alternative */ + +#include "options/honey_options.h" + + +static void print_usage(const char *program_name) +{ + printf("Usage: %s [OPTIONS]\n" + " -v Increase output verbosity (-vvv displays every log message)\n" + " -q Decrease output verbosity (-qqq suppresses even fatal errors)\n" + " -h Print this help message and exit\n" + " -s SCRIPT Load SCRIPT as the entry point instead of 'main.lua'\n", + program_name); +} + + +int parse_options(struct honey_options *opts, int argc, char **argv) +{ + opts->main_script = "main.lua"; + opts->log_level = WARN; + opts->display_help = 0; + + int opt; + const char *flags = "hqvs:"; + while ((opt = getopt(argc, argv, flags)) != -1) { + switch (opt) { + case 'q': + opts->log_level -= 1; + break; + + case 'v': + opts->log_level += 1; + break; + + case 'h': + print_usage(argv[0]); + opts->display_help = 1; + return 1; + + case 's': + opts->main_script = optarg; + break; + + default: + print_usage(argv[0]); + return 0; + } + } + + honey_log_set_level(opts->log_level); + honey_log_set_file(stderr); + + return 1; +} diff --git a/src/options/honey_options.h b/src/options/honey_options.h new file mode 100644 index 0000000..5f4aded --- /dev/null +++ b/src/options/honey_options.h @@ -0,0 +1,14 @@ +#ifndef HONEY_OPTIONS_H +#define HONEY_OPTIONS_H + +#include "logging/logging.h" + +struct honey_options { + const char *main_script; + enum honey_log_level_t log_level; + int display_help; +}; + +int parse_options(struct honey_options *opts, int argc, char **argv); + +#endif |