summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsanine <sanine.not@pm.me>2022-03-01 22:42:10 -0600
committersanine <sanine.not@pm.me>2022-03-01 22:42:10 -0600
commit10288765588673645c1cc0a6e3d2245aff3f9080 (patch)
tree3a56953535c3e26156b41331631d51e25ff6bdb9
parent9c238237597de90c73cc65c3fccf2f49bfaa46b4 (diff)
add basic window functions, option parsing, and main loop
-rw-r--r--CMakeLists.txt3
-rw-r--r--src/gl/honey_gl.c193
-rw-r--r--src/gl/honey_gl.h15
-rw-r--r--src/main.c132
-rw-r--r--src/options/honey_options.c55
-rw-r--r--src/options/honey_options.h14
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
diff --git a/src/main.c b/src/main.c
index 8111ef8..e6311c2 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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