summaryrefslogtreecommitdiff
path: root/src/gl
diff options
context:
space:
mode:
authorsanine <sanine.not@pm.me>2022-08-23 13:38:27 -0500
committersanine <sanine.not@pm.me>2022-08-23 13:38:27 -0500
commit3afbf2a13b2dada445fb667bf25600407fea480a (patch)
tree551329e6f74fc9f177616de0d6739e8b5331ae96 /src/gl
parent261e3f991221fbad6bbf262f5e65b773e4b6c73e (diff)
parent25ed7eb9f84e9a822f698ad803901fbb2a5354cf (diff)
:wMerge branch 'gl-window' into main
Diffstat (limited to 'src/gl')
-rw-r--r--src/gl/CMakeLists.txt20
-rw-r--r--src/gl/data.c156
-rw-r--r--src/gl/data.test.c216
-rw-r--r--src/gl/drawing.c81
-rw-r--r--src/gl/gl.c83
-rw-r--r--src/gl/gl.h14
-rw-r--r--src/gl/gl.test.c237
-rw-r--r--src/gl/shader.c180
-rw-r--r--src/gl/texture.c116
-rw-r--r--src/gl/window.c233
-rw-r--r--src/gl/window.test.c430
11 files changed, 1264 insertions, 502 deletions
diff --git a/src/gl/CMakeLists.txt b/src/gl/CMakeLists.txt
new file mode 100644
index 0000000..c1c3db0
--- /dev/null
+++ b/src/gl/CMakeLists.txt
@@ -0,0 +1,20 @@
+project(honey_engine)
+
+set (GL ${CMAKE_CURRENT_LIST_DIR})
+
+target_sources(honey PUBLIC
+ ${GL}/data.c
+ ${GL}/drawing.c
+ ${GL}/shader.c
+ ${GL}/window.c
+ ${GL}/texture.c
+ ${GL}/gl.c
+ ${GL}/glad/glad.c
+)
+
+
+target_sources(test PUBLIC
+ ${GL}/glad/glad.c
+ ${GL}/gl.test.c
+ ${GL}/window.test.c
+)
diff --git a/src/gl/data.c b/src/gl/data.c
new file mode 100644
index 0000000..a9d0324
--- /dev/null
+++ b/src/gl/data.c
@@ -0,0 +1,156 @@
+#include <stdlib.h>
+#include <stdbool.h>
+#include "gl/glad/glad.h"
+#include <GLFW/glfw3.h>
+#include <lua.h>
+#include <honeysuckle.h>
+#include "util/util.h"
+#include "gl.h"
+
+int gl_create_buffer(lua_State *L);
+int gl_bind_buffer(lua_State *L);
+int gl_buffer_data(lua_State *L);
+
+int gl_vertex_array_create(lua_State *L);
+int gl_vertex_array_bind(lua_State *L);
+int gl_vertex_attrib_pointer(lua_State *L);
+int gl_vertex_array_enable_attrib(lua_State *L);
+
+
+void setup_data(lua_State *L, int gl_index)
+{
+ int tbl = hs_create_table(L,
+ /* functions */
+ hs_str_cfunc("GenBuffers", gl_create_buffer),
+ hs_str_cfunc("BindBuffer", gl_bind_buffer),
+ hs_str_cfunc("BufferData", gl_buffer_data),
+
+ hs_str_cfunc("GenVertexArrays", gl_vertex_array_create),
+ hs_str_cfunc("BindVertexArray", gl_vertex_array_bind),
+ hs_str_cfunc("VertexAttribPointer", gl_vertex_attrib_pointer),
+ hs_str_cfunc("EnableVertexAttribArray", gl_vertex_array_enable_attrib),
+
+ /******** enums ********/
+ /* buffer bind targets */
+ hs_str_int("ARRAY_BUFFER", GL_ARRAY_BUFFER),
+ hs_str_int("ELEMENT_ARRAY_BUFFER", GL_ELEMENT_ARRAY_BUFFER),
+
+ /* buffer usage patters */
+ hs_str_int("STREAM_DRAW", GL_STREAM_DRAW),
+ hs_str_int("STATIC_DRAW", GL_STATIC_DRAW),
+ hs_str_int("DYNAMIC_DRAW", GL_DYNAMIC_DRAW),
+ );
+
+ append_table(L, gl_index, tbl);
+ lua_pop(L, 1);
+}
+
+
+int gl_create_buffer(lua_State *L)
+{
+ int buf;
+ glGenBuffers(1, &buf);
+ lua_pushinteger(L, buf);
+ return 1;
+}
+
+
+int gl_bind_buffer(lua_State *L)
+{
+ lua_Integer target, buf;
+ hs_parse_args(L, hs_int(target), hs_int(buf));
+ glBindBuffer(target, buf);
+ return 0;
+}
+
+
+#define GET_BUFFER_TYPE(type, name, conversion) \
+ void * get_buffer_ ## name (lua_State *L, size_t *sz, int tbl) { \
+ size_t len = lua_objlen(L, tbl); \
+ *sz = len * sizeof(type); \
+ type *buf = malloc(*sz); \
+ if (buf == NULL) \
+ hs_throw_error(L, "failed to allocate intermediary buffer"); \
+ for (int i=0; i<len; i++) { \
+ lua_rawgeti(L, tbl, i+1); \
+ if (!lua_isnumber(L, -1)) \
+ hs_throw_error(L, "all elements must be numbers (failed at index %d)", i); \
+ buf[i] = conversion(L, -1); \
+ lua_pop(L, 1); \
+ } \
+ return buf; \
+ }
+
+GET_BUFFER_TYPE(unsigned int, uint, lua_tointeger)
+GET_BUFFER_TYPE(int, int, lua_tointeger)
+GET_BUFFER_TYPE(float, float, lua_tonumber)
+
+int gl_buffer_data(lua_State *L)
+{
+ lua_Integer target, type, usage;
+ int table;
+ hs_parse_args(L, hs_int(target), hs_int(type), hs_tbl(table), hs_int(usage));
+
+ /* build raw buffer */
+ void *buf; size_t len;
+ switch(type) {
+ case GL_UNSIGNED_INT:
+ buf = get_buffer_uint(L, &len, table);
+ break;
+
+ case GL_INT:
+ buf = get_buffer_int(L, &len, table);
+ break;
+
+ case GL_FLOAT:
+ buf = get_buffer_float(L, &len, table);
+ break;
+
+ default:
+ hs_throw_error(L, "invalid type");
+ }
+
+ /* call */
+ glBufferData(target, len, buf, usage);
+ free(buf);
+ return 0;
+}
+
+
+int gl_vertex_array_create(lua_State *L)
+{
+ int array;
+ glGenVertexArrays(1, &array);
+ lua_pushinteger(L, array);
+ return 1;
+}
+
+
+int gl_vertex_array_bind(lua_State *L)
+{
+ lua_Integer array;
+ hs_parse_args(L, hs_int(array));
+ glBindVertexArray(array);
+ return 0;
+}
+
+
+int gl_vertex_attrib_pointer(lua_State *L)
+{
+ lua_Integer index, size, stride, offset;
+ bool normalized;
+ hs_parse_args(L, hs_int(index), hs_int(size), hs_bool(normalized), hs_int(stride), hs_int(offset));
+ glVertexAttribPointer(index, size, GL_FLOAT,
+ normalized, stride*sizeof(float),
+ (void*) (offset*sizeof(float)));
+ return 0;
+}
+
+
+int gl_vertex_array_enable_attrib(lua_State *L)
+{
+ lua_Integer index;
+ hs_parse_args(L, hs_int(index));
+ glEnableVertexAttribArray(index);
+ return 0;
+}
diff --git a/src/gl/data.test.c b/src/gl/data.test.c
new file mode 100644
index 0000000..143b0ce
--- /dev/null
+++ b/src/gl/data.test.c
@@ -0,0 +1,216 @@
+#include <string.h>
+#include <lua.h>
+#include <lauxlib.h>
+#include <honeysuckle.h>
+#include "test/honey-test.h"
+
+
+int mock_glfwInit_(void);
+int mock_hs_throw_error_(lua_State *L, const char *str, ...);
+void mock_glfwTerminate_();
+void mock_glBufferData_(int, size_t, const void *, int);
+
+#define DUMMY_FUNCTION(...)
+
+#define glfwInit mock_glfwInit_
+#define hs_throw_error mock_hs_throw_error_
+#define glfwTerminate mock_glfwTerminate_
+#undef glBufferData
+#define glBufferData mock_glBufferData_
+#define setup_shader DUMMY_FUNCTION
+#define setup_drawing DUMMY_FUNCTION
+#include "gl/gl.c"
+#undef glBufferData
+#undef glfwTerminate
+#undef hs_throw_error
+#undef glfwInit
+
+
+lily_mock_t *mock_hs_throw_error = NULL;
+static int mock_hs_throw_error_(lua_State *L, const char *str, ...)
+{
+ struct lily_mock_arg_t args[] = {
+ { sizeof(const char *), &str }
+ };
+ lily_mock_store_call(mock_hs_throw_error, args);
+
+ lua_pushstring(L, "some error");
+ lua_error(L);
+
+ return 0;
+}
+
+lily_mock_t *mock_glBufferData = NULL;
+static void mock_glBufferData_(int target, size_t size, const void *data, int usage)
+{
+ struct lily_mock_arg_t args[] = {
+ { sizeof(int), &target },
+ { sizeof(size_t), &size },
+ { sizeof(int), &usage }
+ };
+ lily_mock_store_call(mock_glBufferData, args);
+
+ int use_ints; lily_get_value(mock_glBufferData, int, &use_ints);
+
+ if (use_ints) {
+ size_t count = size/sizeof(int);
+ int *numbers = data;
+ for (int i=0; i<count; i++) {
+ lily_store_value(mock_glBufferData, int, numbers[i]);
+ }
+ }
+ else {
+ size_t count = size/sizeof(float);
+ float *numbers = data;
+ for (int i=0; i<count; i++) {
+ lily_store_value(mock_glBufferData, float, numbers[i]);
+ }
+ }
+}
+
+
+/* ~~~~~~~~ suite ~~~~~~~~ */
+
+void gl_init_succeeds()
+{
+ lily_mock_use(&mock_glfwInit);
+ lily_mock_use(&mock_hs_throw_error);
+
+ lua_State *L = luaL_newstate();
+ lily_store_value(mock_glfwInit, int, 1);
+ lua_pushcfunction(L, gl_init);
+ int err = lua_pcall(L, 0, 0, 0);
+ lua_close(L);
+
+ lily_assert_int_equal(err, 0);
+ lily_assert_int_equal(mock_glfwInit->n_calls, 1);
+ lily_assert_int_equal(mock_hs_throw_error->n_calls, 0);
+}
+
+
+void gl_init_fails()
+{
+ lily_mock_use(&mock_glfwInit);
+ lily_mock_use(&mock_hs_throw_error);
+
+ lua_State *L = luaL_newstate();
+ lily_store_value(mock_glfwInit, int, 0);
+ lua_pushcfunction(L, gl_init);
+ int err = lua_pcall(L, 0, 0, 0);
+ lua_close(L);
+
+ lily_assert_int_equal(err, LUA_ERRRUN);
+ lily_assert_int_equal(mock_hs_throw_error->n_calls, 1);
+}
+
+
+void gl_terminate_works()
+{
+ lily_mock_use(&mock_glfwTerminate);
+
+ lua_State *L = luaL_newstate();
+ lua_pushcfunction(L, gl_terminate);
+ int err = lua_pcall(L, 0, 0, 0);
+ lua_close(L);
+
+ lily_assert_int_equal(err, 0);
+ lily_assert_int_equal(mock_glfwTerminate->n_calls, 1);
+}
+
+
+void gl_buffer_float_works()
+{
+ lily_mock_use(&mock_hs_throw_error);
+ lily_mock_use(&mock_glBufferData);
+ lily_store_value(mock_glBufferData, int, 0); // use floats
+
+ lua_State *L = luaL_newstate();
+ lua_pushcfunction(L, gl_buffer_data);
+ lua_pushinteger(L, GL_ARRAY_BUFFER);
+ lua_pushinteger(L, GL_FLOAT);
+ hs_create_table(L,
+ hs_int_num(1, 22),
+ hs_int_num(2, 33),
+ hs_int_num(3, 44),
+ );
+ lua_pushinteger(L, GL_STATIC_DRAW);
+ int err = hs_call(L, 4, 0);
+ lua_close(L);
+
+ lily_assert_int_equal(err, 0);
+ lily_assert_int_equal(mock_glBufferData->n_calls, 1);
+ int target; size_t size; int usage;
+ struct lily_mock_arg_t args[] = {
+ { sizeof(int), &target },
+ { sizeof(size_t), &size },
+ { sizeof(int), &usage }
+ };
+ lily_mock_get_call(mock_glBufferData, args, 0);
+ lily_assert_int_equal(target, GL_ARRAY_BUFFER);
+ lily_assert_int_equal(size, 3*sizeof(float));
+ lily_assert_int_equal(usage, GL_STATIC_DRAW);
+
+ float n;
+ lily_get_value(mock_glBufferData, float, &n);
+ lily_assert_float_equal(n, 22, 0.1);
+ lily_get_value(mock_glBufferData, float, &n);
+ lily_assert_float_equal(n, 33, 0.1);
+ lily_get_value(mock_glBufferData, float, &n);
+ lily_assert_float_equal(n, 44, 0.1);
+}
+
+
+void gl_buffer_int_works()
+{
+ lily_mock_use(&mock_hs_throw_error);
+ lily_mock_use(&mock_glBufferData);
+ lily_store_value(mock_glBufferData, int, 1); // use ints
+
+ lua_State *L = luaL_newstate();
+ lua_pushcfunction(L, gl_buffer_data);
+ lua_pushinteger(L, GL_ARRAY_BUFFER);
+ lua_pushinteger(L, GL_INT);
+ hs_create_table(L,
+ hs_int_num(1, 22),
+ hs_int_num(2, 33),
+ hs_int_num(3, 44),
+ );
+ lua_pushinteger(L, GL_STATIC_DRAW);
+ int err = hs_call(L, 4, 0);
+ lua_close(L);
+
+ lily_assert_int_equal(err, 0);
+ lily_assert_int_equal(mock_glBufferData->n_calls, 1);
+ int target; size_t size; int usage;
+ struct lily_mock_arg_t args[] = {
+ { sizeof(int), &target },
+ { sizeof(size_t), &size },
+ { sizeof(int), &usage }
+ };
+ lily_mock_get_call(mock_glBufferData, args, 0);
+ lily_assert_int_equal(target, GL_ARRAY_BUFFER);
+ lily_assert_int_equal(size, 3*sizeof(float));
+ lily_assert_int_equal(usage, GL_STATIC_DRAW);
+
+ int n;
+ lily_get_value(mock_glBufferData, int, &n);
+ lily_assert_int_equal(n, 22);
+ lily_get_value(mock_glBufferData, int, &n);
+ lily_assert_int_equal(n, 33);
+ lily_get_value(mock_glBufferData, int, &n);
+ lily_assert_int_equal(n, 44);
+}
+
+void suite_gl()
+{
+ lily_run_test(gl_init_succeeds);
+ lily_run_test(gl_init_fails);
+ lily_run_test(gl_terminate_works);
+ lily_run_test(gl_buffer_float_works);
+ lily_run_test(gl_buffer_int_works);
+
+ lily_mock_destroy(mock_glfwInit);
+ lily_mock_destroy(mock_hs_throw_error);
+ lily_mock_destroy(mock_glfwTerminate);
+ lily_mock_destroy(mock_glBufferData);
+}
diff --git a/src/gl/drawing.c b/src/gl/drawing.c
new file mode 100644
index 0000000..e6b1e13
--- /dev/null
+++ b/src/gl/drawing.c
@@ -0,0 +1,81 @@
+#include "gl/glad/glad.h"
+#include <GLFW/glfw3.h>
+#include <lua.h>
+#include <honeysuckle.h>
+#include "util/util.h"
+
+int gl_set_viewport(lua_State *L);
+int gl_draw_arrays(lua_State *L);
+int gl_draw_elements(lua_State *L);
+int gl_set_clear_color(lua_State *L);
+int gl_clear(lua_State *L);
+
+void setup_drawing(lua_State *L, int gl_index)
+{
+ int tbl = hs_create_table(L,
+ /* functions */
+ hs_str_cfunc("DrawArrays", gl_draw_arrays),
+ hs_str_cfunc("DrawElements", gl_draw_elements),
+ hs_str_cfunc("ClearColor", gl_set_clear_color),
+ hs_str_cfunc("Clear", gl_clear),
+ hs_str_cfunc("Viewport", gl_set_viewport),
+
+ /******** enums ********/
+ /* rendering primitives */
+ hs_str_int("POINTS", GL_POINTS),
+ hs_str_int("LINES", GL_LINES),
+ hs_str_int("TRIANGLES", GL_TRIANGLES),
+
+ /* clear bitmasks */
+ hs_str_int("COLOR_BUFFER_BIT", GL_COLOR_BUFFER_BIT),
+ hs_str_int("DEPTH_BUFFER_BIT", GL_DEPTH_BUFFER_BIT),
+ hs_str_int("STENCIL_BUFFER_BIT", GL_STENCIL_BUFFER_BIT),
+ );
+
+ append_table(L, gl_index, tbl);
+ lua_pop(L, 1);
+}
+
+int gl_set_clear_color(lua_State *L)
+{
+ lua_Number r, g, b, a;
+ hs_parse_args(L, hs_num(r), hs_num(g), hs_num(b), hs_num(a));
+ glClearColor(r, g, b, a);
+ return 0;
+}
+
+
+int gl_clear(lua_State *L)
+{
+ lua_Integer mask;
+ hs_parse_args(L, hs_int(mask));
+ glClear(mask);
+ return 0;
+}
+
+
+int gl_draw_arrays(lua_State *L)
+{
+ lua_Integer mode, first, count;
+ hs_parse_args(L, hs_int(mode), hs_int(first), hs_int(count));
+ glDrawArrays(mode, first, count);
+ return 0;
+}
+
+
+int gl_draw_elements(lua_State *L)
+{
+ lua_Integer mode, count, type, offset;
+ hs_parse_args(L, hs_int(mode), hs_int(count), hs_int(type), hs_int(offset));
+ glDrawElements(mode, count, type, (const void*)offset);
+ return 0;
+}
+
+
+int gl_set_viewport(lua_State *L)
+{
+ lua_Integer x, y, w, h;
+ hs_parse_args(L, hs_int(x), hs_int(y), hs_int(w), hs_int(h));
+ glViewport(x, y, w, h);
+ return 0;
+}
diff --git a/src/gl/gl.c b/src/gl/gl.c
new file mode 100644
index 0000000..4639d48
--- /dev/null
+++ b/src/gl/gl.c
@@ -0,0 +1,83 @@
+#include <stdlib.h>
+#include <stdbool.h>
+#include "gl/glad/glad.h"
+#include <GLFW/glfw3.h>
+#include <lua.h>
+#include <honeysuckle.h>
+#include "gl.h"
+
+/* needs to be here because glad uses macros to define glBufferData */
+#ifdef HONEY_TEST_H
+#undef glBufferData
+#define glBufferData mock_glBufferData_
+#endif
+
+
+int gl_init(lua_State *L);
+int glad_init(lua_State *L);
+int gl_terminate(lua_State *L);
+int gl_get_error(lua_State *L);
+
+void setup_gl(lua_State *L, int honey_index)
+{
+ int gl_index = hs_create_table(L,
+ /* functions */
+ hs_str_cfunc("Init", gl_init),
+ hs_str_cfunc("InitGlad", glad_init),
+ hs_str_cfunc("Terminate", gl_terminate),
+ hs_str_cfunc("GetError", gl_get_error),
+
+ /******** enums ********/
+ /* data types */
+ hs_str_int("UNSIGNED_BYTE", GL_UNSIGNED_BYTE),
+ hs_str_int("UNSIGNED_INT", GL_UNSIGNED_INT),
+ hs_str_int("INT", GL_INT),
+ hs_str_int("FLOAT", GL_FLOAT),
+
+ /* error types */
+ hs_str_int("NO_ERROR", GL_NO_ERROR),
+ hs_str_int("INVALID_ENUM", GL_INVALID_ENUM),
+ hs_str_int("INVALID_VALUE", GL_INVALID_VALUE),
+ hs_str_int("INVALID_OPERATION", GL_INVALID_OPERATION),
+ hs_str_int("INVALID_FRAMEBUFFER_OPERATION", GL_INVALID_FRAMEBUFFER_OPERATION),
+ hs_str_int("OUT_OF_MEMORY", GL_OUT_OF_MEMORY),
+ );
+
+ setup_shader(L, gl_index);
+ setup_drawing(L, gl_index);
+ setup_data(L, gl_index);
+ setup_texture(L, gl_index);
+
+ lua_setfield(L, honey_index, "gl");
+}
+
+
+int gl_init(lua_State *L)
+{
+ if (!glfwInit()) {
+ hs_throw_error(L, "failed to initialize GLFW");
+ }
+ return 0;
+}
+
+
+int glad_init(lua_State *L)
+{
+ if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
+ hs_throw_error(L, "failed to initialize GLAD");
+ }
+}
+
+
+int gl_terminate(lua_State *L)
+{
+ glfwTerminate();
+ return 0;
+}
+
+
+int gl_get_error(lua_State *L)
+{
+ lua_pushinteger(L, glGetError());
+ return 1;
+}
diff --git a/src/gl/gl.h b/src/gl/gl.h
new file mode 100644
index 0000000..8aa1ef7
--- /dev/null
+++ b/src/gl/gl.h
@@ -0,0 +1,14 @@
+#ifndef HONEY_GL_H
+#define HONEY_GL_H
+
+#include <lua.h>
+
+void setup_gl(lua_State *L, int honey_index);
+void setup_shader(lua_State *L, int gl_index);
+void setup_drawing(lua_State *L, int gl_index);
+void setup_data(lua_State *L, int gl_index);
+void setup_texture(lua_State *L, int gl_index);
+
+void setup_window(lua_State *L, int honey_index);
+
+#endif
diff --git a/src/gl/gl.test.c b/src/gl/gl.test.c
new file mode 100644
index 0000000..488126f
--- /dev/null
+++ b/src/gl/gl.test.c
@@ -0,0 +1,237 @@
+#include <string.h>
+#include <lua.h>
+#include <lauxlib.h>
+#include <honeysuckle.h>
+#include "test/honey-test.h"
+
+
+int mock_glfwInit_(void);
+int mock_hs_throw_error_(lua_State *L, const char *str, ...);
+void mock_glfwTerminate_();
+void mock_glBufferData_(int, size_t, const void *, int);
+
+#define DUMMY_FUNCTION(...)
+
+#define glfwInit mock_glfwInit_
+#define hs_throw_error mock_hs_throw_error_
+#define glfwTerminate mock_glfwTerminate_
+#define setup_shader DUMMY_FUNCTION
+#define setup_drawing DUMMY_FUNCTION
+#define setup_texture DUMMY_FUNCTION
+#include "gl/gl.c"
+#include "gl/data.c"
+#undef glBufferData
+#undef glfwTerminate
+#undef hs_throw_error
+#undef glfwInit
+
+
+lily_mock_t *mock_glfwInit = NULL;
+int mock_glfwInit_()
+{
+ struct lily_mock_arg_t args[] = {};
+ lily_mock_store_call(mock_glfwInit, args);
+
+ int result;
+ lily_get_value(mock_glfwInit, int, &result);
+ return result;
+}
+
+
+lily_mock_t *mock_hs_throw_error = NULL;
+int mock_hs_throw_error_(lua_State *L, const char *str, ...)
+{
+ struct lily_mock_arg_t args[] = {
+ { sizeof(const char *), &str }
+ };
+ lily_mock_store_call(mock_hs_throw_error, args);
+
+ lua_pushstring(L, "some error");
+ lua_error(L);
+
+ return 0;
+}
+
+
+lily_mock_t *mock_glfwTerminate = NULL;
+void mock_glfwTerminate_()
+{
+ struct lily_mock_arg_t args[] = {};
+ lily_mock_store_call(mock_glfwTerminate, args);
+}
+
+
+lily_mock_t *mock_glBufferData = NULL;
+void mock_glBufferData_(int target, size_t size, const void *data, int usage)
+{
+ struct lily_mock_arg_t args[] = {
+ { sizeof(int), &target },
+ { sizeof(size_t), &size },
+ { sizeof(int), &usage }
+ };
+ lily_mock_store_call(mock_glBufferData, args);
+
+ int use_ints; lily_get_value(mock_glBufferData, int, &use_ints);
+
+ if (use_ints) {
+ size_t count = size/sizeof(int);
+ int *numbers = data;
+ for (int i=0; i<count; i++) {
+ lily_store_value(mock_glBufferData, int, numbers[i]);
+ }
+ }
+ else {
+ size_t count = size/sizeof(float);
+ float *numbers = data;
+ for (int i=0; i<count; i++) {
+ lily_store_value(mock_glBufferData, float, numbers[i]);
+ }
+ }
+}
+
+
+/* ~~~~~~~~ suite ~~~~~~~~ */
+
+void gl_init_succeeds()
+{
+ lily_mock_use(&mock_glfwInit);
+ lily_mock_use(&mock_hs_throw_error);
+
+ lua_State *L = luaL_newstate();
+ lily_store_value(mock_glfwInit, int, 1);
+ lua_pushcfunction(L, gl_init);
+ int err = lua_pcall(L, 0, 0, 0);
+ lua_close(L);
+
+ lily_assert_int_equal(err, 0);
+ lily_assert_int_equal(mock_glfwInit->n_calls, 1);
+ lily_assert_int_equal(mock_hs_throw_error->n_calls, 0);
+}
+
+
+void gl_init_fails()
+{
+ lily_mock_use(&mock_glfwInit);
+ lily_mock_use(&mock_hs_throw_error);
+
+ lua_State *L = luaL_newstate();
+ lily_store_value(mock_glfwInit, int, 0);
+ lua_pushcfunction(L, gl_init);
+ int err = lua_pcall(L, 0, 0, 0);
+ lua_close(L);
+
+ lily_assert_int_equal(err, LUA_ERRRUN);
+ lily_assert_int_equal(mock_hs_throw_error->n_calls, 1);
+}
+
+
+void gl_terminate_works()
+{
+ lily_mock_use(&mock_glfwTerminate);
+
+ lua_State *L = luaL_newstate();
+ lua_pushcfunction(L, gl_terminate);
+ int err = lua_pcall(L, 0, 0, 0);
+ lua_close(L);
+
+ lily_assert_int_equal(err, 0);
+ lily_assert_int_equal(mock_glfwTerminate->n_calls, 1);
+}
+
+
+void gl_buffer_float_works()
+{
+ lily_mock_use(&mock_hs_throw_error);
+ lily_mock_use(&mock_glBufferData);
+ lily_store_value(mock_glBufferData, int, 0); // use floats
+
+ lua_State *L = luaL_newstate();
+ lua_pushcfunction(L, gl_buffer_data);
+ lua_pushinteger(L, GL_ARRAY_BUFFER);
+ lua_pushinteger(L, GL_FLOAT);
+ hs_create_table(L,
+ hs_int_num(1, 22),
+ hs_int_num(2, 33),
+ hs_int_num(3, 44),
+ );
+ lua_pushinteger(L, GL_STATIC_DRAW);
+ int err = hs_call(L, 4, 0);
+ lua_close(L);
+
+ lily_assert_int_equal(err, 0);
+ lily_assert_int_equal(mock_glBufferData->n_calls, 1);
+ int target; size_t size; int usage;
+ struct lily_mock_arg_t args[] = {
+ { sizeof(int), &target },
+ { sizeof(size_t), &size },
+ { sizeof(int), &usage }
+ };
+ lily_mock_get_call(mock_glBufferData, args, 0);
+ lily_assert_int_equal(target, GL_ARRAY_BUFFER);
+ lily_assert_int_equal(size, 3*sizeof(float));
+ lily_assert_int_equal(usage, GL_STATIC_DRAW);
+
+ float n;
+ lily_get_value(mock_glBufferData, float, &n);
+ lily_assert_float_equal(n, 22, 0.1);
+ lily_get_value(mock_glBufferData, float, &n);
+ lily_assert_float_equal(n, 33, 0.1);
+ lily_get_value(mock_glBufferData, float, &n);
+ lily_assert_float_equal(n, 44, 0.1);
+}
+
+
+void gl_buffer_int_works()
+{
+ lily_mock_use(&mock_hs_throw_error);
+ lily_mock_use(&mock_glBufferData);
+ lily_store_value(mock_glBufferData, int, 1); // use ints
+
+ lua_State *L = luaL_newstate();
+ lua_pushcfunction(L, gl_buffer_data);
+ lua_pushinteger(L, GL_ARRAY_BUFFER);
+ lua_pushinteger(L, GL_INT);
+ hs_create_table(L,
+ hs_int_num(1, 22),
+ hs_int_num(2, 33),
+ hs_int_num(3, 44),
+ );
+ lua_pushinteger(L, GL_STATIC_DRAW);
+ int err = hs_call(L, 4, 0);
+ lua_close(L);
+
+ lily_assert_int_equal(err, 0);
+ lily_assert_int_equal(mock_glBufferData->n_calls, 1);
+ int target; size_t size; int usage;
+ struct lily_mock_arg_t args[] = {
+ { sizeof(int), &target },
+ { sizeof(size_t), &size },
+ { sizeof(int), &usage }
+ };
+ lily_mock_get_call(mock_glBufferData, args, 0);
+ lily_assert_int_equal(target, GL_ARRAY_BUFFER);
+ lily_assert_int_equal(size, 3*sizeof(float));
+ lily_assert_int_equal(usage, GL_STATIC_DRAW);
+
+ int n;
+ lily_get_value(mock_glBufferData, int, &n);
+ lily_assert_int_equal(n, 22);
+ lily_get_value(mock_glBufferData, int, &n);
+ lily_assert_int_equal(n, 33);
+ lily_get_value(mock_glBufferData, int, &n);
+ lily_assert_int_equal(n, 44);
+}
+
+void suite_gl()
+{
+ lily_run_test(gl_init_succeeds);
+ lily_run_test(gl_init_fails);
+ lily_run_test(gl_terminate_works);
+ lily_run_test(gl_buffer_float_works);
+ lily_run_test(gl_buffer_int_works);
+
+ lily_mock_destroy(mock_glfwInit);
+ lily_mock_destroy(mock_hs_throw_error);
+ lily_mock_destroy(mock_glfwTerminate);
+ lily_mock_destroy(mock_glBufferData);
+}
diff --git a/src/gl/shader.c b/src/gl/shader.c
new file mode 100644
index 0000000..3732aff
--- /dev/null
+++ b/src/gl/shader.c
@@ -0,0 +1,180 @@
+#include "gl/glad/glad.h"
+#include <GLFW/glfw3.h>
+#include <lua.h>
+#include <honeysuckle.h>
+#include "util/util.h"
+
+int gl_create_shader(lua_State *L);
+int gl_shader_set_source(lua_State *L);
+int gl_shader_compile(lua_State *L);
+int gl_shader_delete(lua_State *L);
+
+int gl_program_create(lua_State *L);
+int gl_program_attach_shader(lua_State *L);
+int gl_program_link(lua_State *L);
+int gl_program_use(lua_State *L);
+
+int gl_uniform_get_location(lua_State *L);
+int gl_uniform_1i(lua_State *L);
+int gl_uniform_4f(lua_State *L);
+
+int gl_uniform_matrix_4fv(lua_State *L);
+
+
+void setup_shader(lua_State *L, int gl_index)
+{
+ int tbl = hs_create_table(L,
+ /* functions */
+ hs_str_cfunc("CreateShader", gl_create_shader),
+ hs_str_cfunc("ShaderSource", gl_shader_set_source),
+ hs_str_cfunc("CompileShader", gl_shader_compile),
+ hs_str_cfunc("DeleteShader", gl_shader_delete),
+
+ hs_str_cfunc("CreateProgram", gl_program_create),
+ hs_str_cfunc("AttachShader", gl_program_attach_shader),
+ hs_str_cfunc("LinkProgram", gl_program_link),
+ hs_str_cfunc("UseProgram", gl_program_use),
+
+ hs_str_cfunc("GetUniformLocation", gl_uniform_get_location),
+ hs_str_cfunc("Uniform1i", gl_uniform_1i),
+ hs_str_cfunc("Uniform4f", gl_uniform_4f),
+
+ hs_str_cfunc("UniformMatrix4fv", gl_uniform_matrix_4fv),
+
+ /******** enums ********/
+ /* shader types */
+ hs_str_int("VERTEX_SHADER", GL_VERTEX_SHADER),
+ hs_str_int("FRAGMENT_SHADER", GL_FRAGMENT_SHADER),
+ );
+
+ append_table(L, gl_index, tbl);
+ lua_pop(L, 1);
+}
+
+
+int gl_create_shader(lua_State *L)
+{
+ lua_Integer type;
+ hs_parse_args(L, hs_int(type));
+ lua_Integer shader = glCreateShader(type);
+ lua_pushinteger(L, shader);
+ return 1;
+}
+
+
+int gl_shader_set_source(lua_State *L)
+{
+ lua_Integer shader;
+ char *code;
+ hs_parse_args(L, hs_int(shader), hs_str(code));
+ glShaderSource(shader, 1, (const GLchar * const*)&code, NULL);
+ return 0;
+}
+
+
+int gl_shader_compile(lua_State *L)
+{
+ lua_Integer shader;
+ hs_parse_args(L, hs_int(shader));
+ glCompileShader(shader);
+ int success; char log[1024];
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
+ if (!success) {
+ glGetShaderInfoLog(shader, 1024, NULL, log);
+ hs_throw_error(L, "shader compilation failed: %s", log);
+ }
+ return 0;
+}
+
+
+int gl_shader_delete(lua_State *L)
+{
+ lua_Integer shader;
+ hs_parse_args(L, hs_int(shader));
+ glDeleteShader(shader);
+ return 0;
+}
+
+
+int gl_program_create(lua_State *L)
+{
+ lua_Integer program = glCreateProgram();
+ lua_pushinteger(L, program);
+ return 1;
+}
+
+
+int gl_program_attach_shader(lua_State *L)
+{
+ lua_Integer program, shader;
+ hs_parse_args(L, hs_int(program), hs_int(shader)),
+ glAttachShader(program, shader);
+ return 0;
+}
+
+
+int gl_program_link(lua_State *L)
+{
+ lua_Integer program;
+ hs_parse_args(L, hs_int(program));
+ glLinkProgram(program);
+ int success; char log[1024];
+ glGetProgramiv(program, GL_LINK_STATUS, &success);
+ if (!success) {
+ glGetProgramInfoLog(program, 1024, NULL, log);
+ hs_throw_error(L, "shader linking failed: %s", log);
+ }
+ return 0;
+}
+
+
+int gl_program_use(lua_State *L)
+{
+ lua_Integer program;
+ hs_parse_args(L, hs_int(program));
+ glUseProgram(program);
+ return 0;
+}
+
+
+int gl_uniform_get_location(lua_State *L)
+{
+ lua_Integer program;
+ char *name;
+ hs_parse_args(L, hs_int(program), hs_str(name));
+ int location = glGetUniformLocation(program, (const GLchar*)name);
+ lua_pushinteger(L, location);
+ return 1;
+}
+
+
+int gl_uniform_1i(lua_State *L)
+{
+ lua_Integer location, v0;
+ hs_parse_args(L, hs_int(location), hs_int(v0));
+ glUniform1i(location, v0);
+ return 0;
+}
+
+
+int gl_uniform_4f(lua_State *L)
+{
+ lua_Integer location;
+ lua_Number v0, v1, v2, v3;
+ hs_parse_args(L, hs_int(location), hs_num(v0), hs_num(v1), hs_num(v2), hs_num(v3));
+ glUniform4f(location, v0, v1, v2, v3);
+ return 0;
+}
+
+
+int gl_uniform_matrix_4fv(lua_State *L)
+{
+ lua_Integer location;
+ bool transpose;
+ void *ptr;
+ hs_parse_args(L, hs_int(location), hs_bool(transpose), hs_user(ptr));
+
+ float *value = ptr;
+ glUniformMatrix4fv(location, 1, transpose, value);
+ return 0;
+}
diff --git a/src/gl/texture.c b/src/gl/texture.c
new file mode 100644
index 0000000..f9a65b3
--- /dev/null
+++ b/src/gl/texture.c
@@ -0,0 +1,116 @@
+#include "gl/glad/glad.h"
+#include <GLFW/glfw3.h>
+#include <lua.h>
+#include <honeysuckle.h>
+#include "util/util.h"
+
+
+int gl_texture_create(lua_State *L);
+int gl_texture_bind(lua_State *L);
+int gl_texture_image_2d(lua_State *L);
+int gl_texture_generate_mipmaps(lua_State *L);
+int gl_texture_set_active(lua_State *L);
+int gl_tex_parameter_i(lua_State *L);
+
+
+void setup_texture(lua_State *L, int gl_index)
+{
+ int tbl = hs_create_table(L,
+ /* functions */
+ hs_str_cfunc("GenTextures", gl_texture_create),
+ hs_str_cfunc("BindTexture", gl_texture_bind),
+ hs_str_cfunc("TexImage2D", gl_texture_image_2d),
+ hs_str_cfunc("GenerateMipmap", gl_texture_generate_mipmaps),
+ hs_str_cfunc("ActiveTexture", gl_texture_set_active),
+ hs_str_cfunc("TexParameteri", gl_tex_parameter_i),
+
+ /******** enums ********/
+ /* texture binding targets */
+ hs_str_int("TEXTURE_2D", GL_TEXTURE_2D),
+
+ /* texture data formats */
+ hs_str_int("RGB", GL_RGB),
+ hs_str_int("RGBA", GL_RGBA),
+
+ /* texture parameters */
+ hs_str_int("TEXTURE_WRAP_S", GL_TEXTURE_WRAP_S),
+ hs_str_int("TEXTURE_WRAP_T", GL_TEXTURE_WRAP_T),
+ hs_str_int("TEXTURE_MIN_FILTER", GL_TEXTURE_MIN_FILTER),
+ hs_str_int("TEXTURE_MAG_FILTER", GL_TEXTURE_MAG_FILTER),
+
+ /* wrapping types */
+ hs_str_int("REPEAT", GL_REPEAT),
+
+ /* filter types */
+ hs_str_int("NEAREST", GL_NEAREST),
+ hs_str_int("LINEAR", GL_LINEAR),
+ );
+
+ append_table(L, gl_index, tbl);
+ lua_pop(L, 1);
+}
+
+
+int gl_texture_create(lua_State *L)
+{
+ unsigned int texture;
+ glGenTextures(1, &texture);
+ lua_pushinteger(L, texture);
+ return 1;
+}
+
+
+int gl_texture_bind(lua_State *L)
+{
+ lua_Integer target, texture;
+ hs_parse_args(L, hs_int(target), hs_int(texture));
+ glBindTexture(target, texture);
+ return 0;
+}
+
+
+int gl_texture_image_2d(lua_State *L)
+{
+ lua_Integer target, mipmap_level,
+ internal_format,
+ width, height,
+ format, type;
+ void *data;
+ hs_parse_args(L,
+ hs_int(target), hs_int(mipmap_level),
+ hs_int(internal_format),
+ hs_int(width), hs_int(height),
+ hs_int(format), hs_int(type),
+ hs_light(data)
+ );
+
+ glTexImage2D(target, mipmap_level, internal_format, width, height, 0, format, type, data);
+ return 0;
+}
+
+
+int gl_texture_generate_mipmaps(lua_State *L)
+{
+ lua_Integer target;
+ hs_parse_args(L, hs_int(target));
+ glGenerateMipmap(target);
+ return 0;
+}
+
+
+int gl_texture_set_active(lua_State *L)
+{
+ lua_Integer unit;
+ hs_parse_args(L, hs_int(unit));
+ glActiveTexture(GL_TEXTURE0 + unit);
+ return 0;
+}
+
+
+int gl_tex_parameter_i(lua_State *L)
+{
+ lua_Integer target, pname, param;
+ hs_parse_args(L, hs_int(target), hs_int(pname), hs_int(param));
+ glTexParameteri(target, pname, param);
+ return 0;
+}
diff --git a/src/gl/window.c b/src/gl/window.c
index e16b330..fd13022 100644
--- a/src/gl/window.c
+++ b/src/gl/window.c
@@ -1,104 +1,175 @@
+#include <stdlib.h>
#include "gl/glad/glad.h"
#include <GLFW/glfw3.h>
#include <lua.h>
#include <honeysuckle.h>
-/* build a table of all possible window hints */
-void create_glfw_window_hints_table(lua_State *L)
+struct window_data {
+ lua_State *L;
+ int framebuffer_size_callback;
+};
+
+struct window_data * create_window_data(lua_State *L)
{
- /* hint keys */
- hs_create_table(L,
- /* window hints */
- hs_str_int("resizable", GLFW_RESIZABLE),
- hs_str_int("visible", GLFW_VISIBLE),
- hs_str_int("decorated", GLFW_DECORATED),
- hs_str_int("focused", GLFW_FOCUSED),
- hs_str_int("autoIconify", GLFW_AUTO_ICONIFY),
- hs_str_int("floating", GLFW_FLOATING),
- hs_str_int("maximized", GLFW_MAXIMIZED),
- hs_str_int("centerCursor", GLFW_CENTER_CURSOR),
- hs_str_int("transparentFramebuffer", GLFW_TRANSPARENT_FRAMEBUFFER),
- hs_str_int("focusOnShow", GLFW_FOCUS_ON_SHOW),
- hs_str_int("scaleToMonitor", GLFW_SCALE_TO_MONITOR),
-
- /* framebuffer hints */
- hs_str_int("redBits", GLFW_RED_BITS),
- hs_str_int("greenBits", GLFW_GREEN_BITS),
- hs_str_int("blueBits", GLFW_BLUE_BITS),
- hs_str_int("alphaBits", GLFW_ALPHA_BITS),
- hs_str_int("depthBits", GLFW_DEPTH_BITS),
- hs_str_int("stereoscopic", GLFW_STEREO),
- hs_str_int("samples", GLFW_SAMPLES),
- hs_str_int("srgbCapable", GLFW_SRGB_CAPABLE),
- hs_str_int("doubleBuffer", GLFW_DOUBLEBUFFER),
-
- /* monitor & context hints */
- hs_str_int("refreshRate", GLFW_REFRESH_RATE),
- hs_str_int("clientApi", GLFW_CLIENT_API),
- hs_str_int("contextCreationApi", GLFW_CONTEXT_CREATION_API),
+ struct window_data *wdata = malloc(sizeof(struct window_data));
+ if (wdata == NULL)
+ return NULL;
+ wdata->L = L;
+ wdata->framebuffer_size_callback = LUA_NOREF;
+ return wdata;
+}
+
+int window_create(lua_State *L);
+int window_destroy(lua_State *L);
+int window_make_context_current(lua_State *L);
+int window_set_hint(lua_State *L);
+int window_should_close(lua_State *L);
+int window_poll_events(lua_State *L);
+int window_swap_buffers(lua_State *L);
+int window_set_framebuffer_size_callback(lua_State *L);
+int window_get_time(lua_State *L);
+
+
+
+void setup_window(lua_State *L, int honey_index)
+{
+ int hint_types = hs_create_table(L,
hs_str_int("contextVersionMajor", GLFW_CONTEXT_VERSION_MAJOR),
hs_str_int("contextVersionMinor", GLFW_CONTEXT_VERSION_MINOR),
- hs_str_int("forwardCompatible", GLFW_OPENGL_FORWARD_COMPAT),
- hs_str_int("debugContext", GLFW_OPENGL_DEBUG_CONTEXT),
- hs_str_int("profile", GLFW_OPENGL_PROFILE),
- hs_str_int("contextRobustness", GLFW_CONTEXT_ROBUSTNESS),
- hs_str_int("contextReleaseBehavior", GLFW_CONTEXT_RELEASE_BEHAVIOR),
- hs_str_int("noError", GLFW_CONTEXT_NO_ERROR)
+ hs_str_int("openGlProfile", GLFW_OPENGL_PROFILE),
+ );
+
+ int profile_types = hs_create_table(L,
+ hs_str_int("openGlCoreProfile", GLFW_OPENGL_CORE_PROFILE),
);
- /* special hint values */
hs_create_table(L,
- hs_str_int("dontCare", GLFW_DONT_CARE),
-
- /* client api */
- hs_str_int("glApi", GLFW_OPENGL_API),
- hs_str_int("glesApi", GLFW_OPENGL_ES_API),
- hs_str_int("noApi", GLFW_NO_API),
-
- /* context api */
- hs_str_int("nativeApi", GLFW_NATIVE_CONTEXT_API),
- hs_str_int("eglApi", GLFW_EGL_CONTEXT_API),
- hs_str_int("osMesaApi", GLFW_OSMESA_CONTEXT_API),
-
- /* robustness */
- hs_str_int("noRobustness", GLFW_NO_ROBUSTNESS),
- hs_str_int("noResetNotification", GLFW_NO_RESET_NOTIFICATION),
- hs_str_int("loseContextOnReset", GLFW_LOSE_CONTEXT_ON_RESET),
-
- /* context release */
- hs_str_int("anyBehavior", GLFW_ANY_RELEASE_BEHAVIOR),
- hs_str_int("flush", GLFW_RELEASE_BEHAVIOR_FLUSH),
- hs_str_int("none", GLFW_RELEASE_BEHAVIOR_NONE),
-
- /* profile */
- hs_str_int("anyProfile", GLFW_OPENGL_ANY_PROFILE),
- hs_str_int("compatabilityProfile", GLFW_OPENGL_COMPAT_PROFILE),
- hs_str_int("coreProfile", GLFW_OPENGL_CORE_PROFILE)
+ hs_str_cfunc("create", window_create),
+ hs_str_cfunc("destroy", window_destroy),
+ hs_str_cfunc("makeContextCurrent", window_make_context_current),
+ hs_str_cfunc("setHint", window_set_hint),
+ hs_str_cfunc("shouldClose", window_should_close),
+ hs_str_cfunc("pollEvents", window_poll_events),
+ hs_str_cfunc("swapBuffers", window_swap_buffers),
+ hs_str_cfunc("setFramebufferSizeCallback", window_set_framebuffer_size_callback),
+ hs_str_cfunc("getTime", window_get_time),
+
+ hs_str_tbl("hintType", hint_types),
+ hs_str_tbl("profileType", profile_types),
);
+ lua_setfield(L, honey_index, "window");
}
-int gl_init(lua_State *L)
+static void framebuffer_size_callback_(GLFWwindow *win, int width, int height)
{
- if (glfwInit() != GLFW_TRUE) {
- hs_throw_error(L, "failed to initialize GLFW");
+ struct window_data *wdata = glfwGetWindowUserPointer(win);
+ if (wdata->framebuffer_size_callback != LUA_NOREF) {
+ hs_rload(wdata->L, wdata->framebuffer_size_callback);
+ lua_pushlightuserdata(wdata->L, win);
+ lua_pushinteger(wdata->L, width);
+ lua_pushinteger(wdata->L, height);
+ hs_call(wdata->L, 3, 0);
}
- return 0;
}
+int window_create(lua_State *L)
+{
+ lua_Integer width, height;
+ char *title;
+ hs_parse_args(L, hs_int(width), hs_int(height), hs_str(title));
+
+ GLFWwindow *win = glfwCreateWindow(width, height, title, NULL, NULL);
+ if (win == NULL)
+ hs_throw_error(L, "failed to create window");
+
+ struct window_data *wdata = create_window_data(L);
+ glfwSetWindowUserPointer(win, wdata);
-lua_Integer tointeger(lua_State *L, int index)
+ glfwSetFramebufferSizeCallback(win, framebuffer_size_callback_);
+
+ lua_pushlightuserdata(L, win);
+ return 1;
+}
+
+
+int window_destroy(lua_State *L)
{
- if (lua_isboolean(L, index)) {
- return lua_toboolean(L, index);
- }
- else if (lua_isnumber(L, index)) {
- return lua_tointeger(L, index);
- }
- else {
- hs_throw_error(L,
- "expected boolean or number; got %s instead",
- lua_typename(L, lua_type(L, index))
- );
+ void *ptr;
+ hs_parse_args(L, hs_light(ptr));
+ GLFWwindow *win = ptr;
+ void *wdata = glfwGetWindowUserPointer(win);
+ if (wdata != NULL) {
+ free(wdata);
+ glfwSetWindowUserPointer(win, NULL);
}
+ glfwDestroyWindow(win);
+ return 0;
+}
+
+
+int window_make_context_current(lua_State *L)
+{
+ void *ptr;
+ hs_parse_args(L, hs_light(ptr));
+ GLFWwindow *win = ptr;
+ glfwMakeContextCurrent(win);
+ return 0;
+}
+
+
+int window_set_hint(lua_State *L)
+{
+ lua_Integer hint, value;
+ hs_parse_args(L, hs_int(hint), hs_int(value));
+ glfwWindowHint(hint, value);
+ return 0;
+}
+
+
+int window_should_close(lua_State *L)
+{
+ void *ptr;
+ hs_parse_args(L, hs_light(ptr));
+ GLFWwindow *win = ptr;
+ lua_pushboolean(L, glfwWindowShouldClose(win));
+ return 1;
+}
+
+
+int window_poll_events(lua_State *L)
+{
+ glfwPollEvents();
+ return 0;
+}
+
+
+int window_swap_buffers(lua_State *L)
+{
+ void *ptr;
+ hs_parse_args(L, hs_light(ptr));
+ GLFWwindow *win = ptr;
+ glfwSwapBuffers(win);
+ return 0;
+}
+
+
+int window_set_framebuffer_size_callback(lua_State *L)
+{
+ void *ptr;
+ int func;
+ hs_parse_args(L, hs_light(ptr), hs_func(func));
+ GLFWwindow *win = ptr;
+ struct window_data *wdata = glfwGetWindowUserPointer(win);
+
+ lua_pushvalue(L, func);
+ wdata->framebuffer_size_callback = hs_rstore(L);
+ return 0;
+}
+
+
+int window_get_time(lua_State *L)
+{
+ lua_pushnumber(L, glfwGetTime());
+ return 1;
}
diff --git a/src/gl/window.test.c b/src/gl/window.test.c
index 6b8e091..505c876 100644
--- a/src/gl/window.test.c
+++ b/src/gl/window.test.c
@@ -1,436 +1,24 @@
-#include <string.h>
+#include <stdlib.h>
#include <lua.h>
-#include <lauxlib.h>
#include <honeysuckle.h>
#include "test/honey-test.h"
-int mock_glfwInit();
-int mock_hs_throw_error(lua_State *L, const char *format_string, ...);
-#define glfwInit mock_glfwInit
-#define hs_throw_error mock_hs_throw_error
-#include "gl/window.c"
-#undef glfwInit
-#undef hs_throw_error
+#include "window.c"
-lily_mock_t *mock_glfwInit_data = NULL;
-int mock_glfwInit()
-{
- lily_mock_call(mock_glfwInit_data, NULL);
-
- int result;
- mock_dequeue(mock_glfwInit, int, &result);
- return result;
-}
-
-lily_mock_t *mock_hs_throw_error_data = NULL;
-int mock_hs_throw_error(lua_State *L, const char *format_string, ...)
-{
- /* to avoid basically just re-implementing printf parsing here,
- i am limiting this function to be able to receive strings only */
-
- /* count format specifiers */
- char *ptr = strchr(format_string, '%');
- int n_args = 0;
- while (ptr != NULL) {
- n_args += 1;
- ptr = strchr(ptr+1, '%');
- }
-
- /* store arguments */
- struct lily_mock_arg_t args[] = {
- { sizeof(const char*), &format_string },
- { sizeof(int), &n_args },
- };
- lily_mock_call(mock_hs_throw_error_data, args);
-
- /* store format arguments */
- va_list vl;
- va_start(vl, format_string);
- for (int i=0; i<n_args; i++) {
- char *str = va_arg(vl, char*);
- mock_enqueue(mock_hs_throw_error, char*, str);
- }
-}
-
-
-/* ~~~~~~~~ TESTS ~~~~~~~~ */
-void gl_init_succeeds();
-void gl_init_fail_glfwInit();
-void glfw_window_hints_table();
-void tointeger_parses_bool();
-void tointeger_parses_int();
-void tointeger_fails_other();
-
-void suite_window()
-{
- lily_run_test(gl_init_succeeds);
- lily_run_test(gl_init_fail_glfwInit);
- lily_run_test(glfw_window_hints_table);
- lily_run_test(tointeger_parses_bool);
- lily_run_test(tointeger_parses_int);
- lily_run_test(tointeger_fails_other);
-
- CLEAN_MOCK(mock_glfwInit);
- CLEAN_MOCK(mock_hs_throw_error);
-}
-
-
-/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
-
-
-void gl_init_succeeds()
-{
- USE_MOCK(mock_glfwInit);
- USE_MOCK(mock_hs_throw_error);
-
- /* queue success */
- mock_enqueue(mock_glfwInit, int, GLFW_TRUE);
- gl_init(NULL);
-
- lily_assert_int_equal(mock_glfwInit_data->n_calls, 1);
- lily_assert_int_equal(mock_hs_throw_error_data->n_calls, 0);
-}
-
-
-void gl_init_fail_glfwInit()
-{
- USE_MOCK(mock_glfwInit);
- USE_MOCK(mock_hs_throw_error);
-
- /* queue failure */
- mock_enqueue(mock_glfwInit, int, GLFW_FALSE);
- gl_init(NULL);
-
- lily_assert_int_equal(mock_glfwInit_data->n_calls, 1);
- lily_assert_int_equal(mock_hs_throw_error_data->n_calls, 1);
-
- const char *fmt; int argc;
- struct lily_mock_arg_t args[] = {
- { sizeof(const char*), &fmt },
- { sizeof(int), &argc },
- };
- lily_get_call(mock_hs_throw_error_data, args, 0);
-
- lily_assert_string_equal((char*) fmt, "failed to initialize GLFW");
-}
-
-
-int get_int(lua_State *L, int table_index, const char *key)
-{
- lua_getfield(L, table_index, key);
- lily_assert_msg(lua_isnumber(L, -1), LILY_LOCATION, "key %s is not a number", key);
- int n = lua_tointeger(L, -1);
- lua_pop(L, 1);
- return n;
-}
-
-#define CHECK_VALUE(str, num) \
- do { \
- int value = get_int(L, 1, str); \
- lily_assert_int_equal(value, num); \
- } while(0)
-
-void glfw_window_hints_table()
+void create_window_data_works()
{
lua_State *L = luaL_newstate();
+ struct window_data *wdata = create_window_data(L);
- lily_assert_int_equal(lua_gettop(L), 0);
- create_glfw_window_hints_table(L);
- lily_assert_int_equal(lua_gettop(L), 2);
- lily_assert_true(lua_istable(L, 1));
-
- /* window hints */
- lily_assert_int_equal(
- get_int(L, 1, "resizable"),
- GLFW_RESIZABLE
- );
-
- lily_assert_int_equal(
- get_int(L, 1, "visible"),
- GLFW_VISIBLE
- );
-
- lily_assert_int_equal(
- get_int(L, 1, "decorated"),
- GLFW_DECORATED
- );
-
- lily_assert_int_equal(
- get_int(L, 1, "focused"),
- GLFW_FOCUSED
- );
-
- lily_assert_int_equal(
- get_int(L, 1, "autoIconify"),
- GLFW_AUTO_ICONIFY
- );
-
- lily_assert_int_equal(
- get_int(L, 1, "floating"),
- GLFW_FLOATING
- );
-
- lily_assert_int_equal(
- get_int(L, 1, "maximized"),
- GLFW_MAXIMIZED
- );
-
- lily_assert_int_equal(
- get_int(L, 1, "centerCursor"),
- GLFW_CENTER_CURSOR
- );
-
- lily_assert_int_equal(
- get_int(L, 1, "transparentFramebuffer"),
- GLFW_TRANSPARENT_FRAMEBUFFER
- );
-
- lily_assert_int_equal(
- get_int(L, 1, "focusOnShow"),
- GLFW_FOCUS_ON_SHOW
- );
-
- lily_assert_int_equal(
- get_int(L, 1, "scaleToMonitor"),
- GLFW_SCALE_TO_MONITOR
- );
-
-
- /* framebuffer hints */
- /* (don't expose accumulation or auxiliary buffer hints) */
- lily_assert_int_equal(
- get_int(L, 1, "redBits"),
- GLFW_RED_BITS
- );
-
- lily_assert_int_equal(
- get_int(L, 1, "greenBits"),
- GLFW_GREEN_BITS
- );
-
- lily_assert_int_equal(
- get_int(L, 1, "blueBits"),
- GLFW_BLUE_BITS
- );
-
- lily_assert_int_equal(
- get_int(L, 1, "alphaBits"),
- GLFW_ALPHA_BITS
- );
-
- lily_assert_int_equal(
- get_int(L, 1, "depthBits"),
- GLFW_DEPTH_BITS
- );
-
- lily_assert_int_equal(
- get_int(L, 1, "stereoscopic"),
- GLFW_STEREO
- );
-
- lily_assert_int_equal(
- get_int(L, 1, "samples"),
- GLFW_SAMPLES
- );
-
- lily_assert_int_equal(
- get_int(L, 1, "srgbCapable"),
- GLFW_SRGB_CAPABLE
- );
-
- lily_assert_int_equal(
- get_int(L, 1, "doubleBuffer"),
- GLFW_DOUBLEBUFFER
- );
-
-
- /* monitor & context hints */
- lily_assert_int_equal(
- get_int(L, 1, "refreshRate"),
- GLFW_REFRESH_RATE
- );
-
- lily_assert_int_equal(
- get_int(L, 1, "clientApi"),
- GLFW_CLIENT_API
- );
-
- lily_assert_int_equal(
- get_int(L, 1, "contextCreationApi"),
- GLFW_CONTEXT_CREATION_API
- );
-
- lily_assert_int_equal(
- get_int(L, 1, "contextVersionMajor"),
- GLFW_CONTEXT_VERSION_MAJOR
- );
-
- lily_assert_int_equal(
- get_int(L, 1, "contextVersionMinor"),
- GLFW_CONTEXT_VERSION_MINOR
- );
-
- lily_assert_int_equal(
- get_int(L, 1, "forwardCompatible"),
- GLFW_OPENGL_FORWARD_COMPAT
- );
-
- lily_assert_int_equal(
- get_int(L, 1, "debugContext"),
- GLFW_OPENGL_DEBUG_CONTEXT
- );
-
- lily_assert_int_equal(
- get_int(L, 1, "profile"),
- GLFW_OPENGL_PROFILE
- );
-
- lily_assert_int_equal(
- get_int(L, 1, "contextRobustness"),
- GLFW_CONTEXT_ROBUSTNESS
- );
-
- lily_assert_int_equal(
- get_int(L, 1, "contextReleaseBehavior"),
- GLFW_CONTEXT_RELEASE_BEHAVIOR
- );
-
- lily_assert_int_equal(
- get_int(L, 1, "noError"),
- GLFW_CONTEXT_NO_ERROR
- );
-
-
- /* special hint values */
-
- lily_assert_int_equal(
- get_int(L, 2, "dontCare"),
- GLFW_DONT_CARE
- );
-
- /* client api */
- lily_assert_int_equal(
- get_int(L, 2, "glApi"),
- GLFW_OPENGL_API
- );
-
- lily_assert_int_equal(
- get_int(L, 2, "glesApi"),
- GLFW_OPENGL_ES_API
- );
-
- lily_assert_int_equal(
- get_int(L, 2, "noApi"),
- GLFW_NO_API
- );
-
- /* context api */
- lily_assert_int_equal(
- get_int(L, 2, "nativeApi"),
- GLFW_NATIVE_CONTEXT_API
- );
-
- lily_assert_int_equal(
- get_int(L, 2, "eglApi"),
- GLFW_EGL_CONTEXT_API
- );
-
- lily_assert_int_equal(
- get_int(L, 2, "osMesaApi"),
- GLFW_OSMESA_CONTEXT_API
- );
-
- /* robustness */
- lily_assert_int_equal(
- get_int(L, 2, "noRobustness"),
- GLFW_NO_ROBUSTNESS
- );
-
- lily_assert_int_equal(
- get_int(L, 2, "noResetNotification"),
- GLFW_NO_RESET_NOTIFICATION
- );
-
- lily_assert_int_equal(
- get_int(L, 2, "loseContextOnReset"),
- GLFW_LOSE_CONTEXT_ON_RESET
- );
-
- /* release */
- lily_assert_int_equal(
- get_int(L, 2, "anyBehavior"),
- GLFW_ANY_RELEASE_BEHAVIOR
- );
-
- lily_assert_int_equal(
- get_int(L, 2, "flush"),
- GLFW_RELEASE_BEHAVIOR_FLUSH
- );
-
- lily_assert_int_equal(
- get_int(L, 2, "none"),
- GLFW_RELEASE_BEHAVIOR_NONE
- );
-
- /* profile */
- lily_assert_int_equal(
- get_int(L, 2, "anyProfile"),
- GLFW_OPENGL_ANY_PROFILE
- );
-
- lily_assert_int_equal(
- get_int(L, 2, "compatabilityProfile"),
- GLFW_OPENGL_COMPAT_PROFILE
- );
-
- lily_assert_int_equal(
- get_int(L, 2, "coreProfile"),
- GLFW_OPENGL_CORE_PROFILE
- );
-
- lua_close(L);
-}
-
-
-void tointeger_parses_bool()
-{
- USE_MOCK(mock_hs_throw_error);
- lua_State *L = luaL_newstate();
-
- lua_pushboolean(L, 0);
- lily_assert_false(lua_toboolean(L, -1));
- lily_assert_int_equal(tointeger(L, -1), 0);
-
- lua_pushboolean(L, 1);
- lily_assert_true(lua_toboolean(L, -1));
- lily_assert_int_equal(tointeger(L, -1), 1);
-
+ lily_assert_ptr_equal(L, wdata->L);
+ lily_assert_int_equal(wdata->framebuffer_size_callback, LUA_NOREF);
lua_close(L);
+ free(wdata);
}
-void tointeger_parses_int()
-{
- USE_MOCK(mock_hs_throw_error);
- lua_State *L = luaL_newstate();
-
- lua_pushinteger(L, 234);
- lua_pushinteger(L, 55555);
-
- lily_assert_int_equal(tointeger(L, -2), 234);
- lily_assert_int_equal(tointeger(L, -1), 55555);
-
- lua_close(L);
-}
-
-
-void tointeger_fails_other()
+void suite_window()
{
- USE_MOCK(mock_hs_throw_error);
- lua_State *L = luaL_newstate();
-
- lua_pushstring(L, "hey there babe");
- tointeger(L, -1);
- lily_assert_int_equal(mock_hs_throw_error_data->n_calls, 1);
+ lily_run_test(create_window_data_works);
}