diff --git a/meson.build b/meson.build index 00cbe5d..8c8fa50 100644 --- a/meson.build +++ b/meson.build @@ -16,7 +16,7 @@ freeimage_dep = cc.find_library('freeimage') executable( 'shadertool', - sources: ['src/main.c', 'src/renderer.c', 'src/shaders.c', 'src/log.c'], + sources: ['src/main.c', 'src/renderer.c', 'src/shaders.c', 'src/io.c', 'src/log.c'], dependencies: [glfw_dep, glew_dep, freeimage_dep], c_args: '-DLOG_USE_COLOR', ) diff --git a/src/io.c b/src/io.c new file mode 100644 index 0000000..96b6b57 --- /dev/null +++ b/src/io.c @@ -0,0 +1,129 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "renderer.h" +#include "shaders.h" + +#define BUF_LEN (10 * (sizeof(struct inotify_event) + 1)) + +/** + * @brief Return the file name without the leading directories and + * without the extension. + * + * @param filename The original filename. + * @return A newly allocated string containing the output of basename(3) + * without the extension. + */ +char *basename_without_suffix(const char *filename) { + char *name = strdup(filename); + name = basename(name); + log_debug("%s", name); + char *dot = strrchr(name, '.'); + if (!dot || dot == name) { + return name; + } + *dot = '\0'; + log_debug("%s", name); + return name; +} + +/** + * @brief Capture a screenshot of the current window. + * + * Takes the dimensions of the viewport to save a pixel array of the + * same dimensions. Uses FreeImage to create an image from the raw + * pixels and save it to disk. + * + * @param state The renderer state, needed to get the name of the + * current shader and the frame count. + */ +void capture_screenshot(struct renderer_state *state) { + time_t now = time(NULL); + struct tm *timenow = gmtime(&now); + char image_filename[255] = {0}; + char *shader_basename = + basename_without_suffix(state->screen_shader.filename); + snprintf(image_filename, sizeof(image_filename), + "%s_%zu_%d%02d%02d_%02d%02d%02d.png", shader_basename, + state->frame_count, timenow->tm_year + 1900, timenow->tm_mon, + timenow->tm_mday, timenow->tm_hour, timenow->tm_min, + timenow->tm_sec); + + int viewport[4] = {0}; + glGetIntegerv(GL_VIEWPORT, viewport); + + GLubyte *pixels = calloc(3 * viewport[2] * viewport[3], 1); + glReadPixels(0, 0, viewport[2], viewport[3], GL_BGR, GL_UNSIGNED_BYTE, + pixels); + + FIBITMAP *image = FreeImage_ConvertFromRawBits( + pixels, viewport[2], viewport[3], 3 * viewport[2], 24, FI_RGBA_RED_MASK, + FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, false); + + if (FreeImage_Save(FIF_PNG, image, image_filename, 0)) { + log_debug("Image saved to %s", image_filename); + } else { + log_error("Failed to saved image to %s", image_filename); + } + + FreeImage_Unload(image); + free(pixels); +} + +/** + * @brief Ensure the window is closed when the user presses the escape + * key. + * + * @param state The current state of the renderer. + */ +void process_input(struct renderer_state *state) { + bool should_reload = false; + + // Skip inotify checking if it's not available + if (state->inotify_fd != -1 && + (state->screen_shader.wd != -1 || state->buffer_shader.wd != -1)) { + char buf[BUF_LEN] = {0}; + int num_read = read(state->inotify_fd, buf, BUF_LEN); + if (num_read == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) { + // No event, do nothing + } else if (num_read <= 0) { + log_error("[inotify] Could not read inotify state"); + } else { + should_reload = true; + } + } + + if (glfwGetKey(state->window, GLFW_KEY_ESCAPE) == GLFW_PRESS) { + log_info("Quitting"); + glfwSetWindowShouldClose(state->window, true); + } else if (should_reload || + glfwGetKey(state->window, GLFW_KEY_R) == GLFW_PRESS) { + if (should_reload) { + log_info("File changed on disk, reloading shaders"); + } else { + log_info("Reloading shaders"); + } + // reinitialize time and frame count + state->frame_count = 0; + state->prev_frame_count = 0; + glfwSetTime(0.0); + state->time = 0.0; + state->prev_time = 0.0; + // recompile shaders + compile_shaders(&state->screen_shader.program, + state->screen_shader.filename); + if (state->buffer_shader.filename) { + compile_shaders(&state->buffer_shader.program, + state->buffer_shader.filename); + } + } else if (glfwGetKey(state->window, GLFW_KEY_S) == GLFW_PRESS) { + capture_screenshot(state); + } +} diff --git a/src/io.h b/src/io.h new file mode 100644 index 0000000..3a5a541 --- /dev/null +++ b/src/io.h @@ -0,0 +1,10 @@ +#ifndef IO_H +#define IO_H + +#include "renderer.h" + +char *basename_without_suffix(const char *filename); +void capture_screenshot(struct renderer_state *state); +void process_input(struct renderer_state *state); + +#endif /* IO_H */ diff --git a/src/main.c b/src/main.c index 4ba3548..f27ac98 100644 --- a/src/main.c +++ b/src/main.c @@ -4,6 +4,7 @@ #include #include +#include "io.h" #include "log.h" #include "renderer.h" #include "shaders.h" diff --git a/src/renderer.c b/src/renderer.c index 7840566..07b06ce 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -1,20 +1,10 @@ -#include #include #include -#include -#include -#include -#include -#include -#include -#include #include "log.h" #include "renderer.h" -#include "shaders.h" #define UNUSED(a) (void)a -#define BUF_LEN (10 * (sizeof(struct inotify_event) + 1)) /** * @brief Initialize GLFW and OpenGL, and create a window. @@ -152,118 +142,3 @@ void framebuffer_size_callback(GLFWwindow *window, int width, int height) { UNUSED(window); glViewport(0, 0, width, height); } - -/** - * @brief Return the file name without the leading directories and - * without the extension. - * - * @param filename The original filename. - * @return A newly allocated string containing the output of basename(3) - * without the extension. - */ -char *basename_without_suffix(const char *filename) { - char *name = strdup(filename); - name = basename(name); - log_debug("%s", name); - char *dot = strrchr(name, '.'); - if (!dot || dot == name) { - return name; - } - *dot = '\0'; - log_debug("%s", name); - return name; -} - -/** - * @brief Capture a screenshot of the current window. - * - * Takes the dimensions of the viewport to save a pixel array of the - * same dimensions. Uses FreeImage to create an image from the raw - * pixels and save it to disk. - * - * @param state The renderer state, needed to get the name of the - * current shader and the frame count. - */ -void capture_screenshot(struct renderer_state *state) { - time_t now = time(NULL); - struct tm *timenow = gmtime(&now); - char image_filename[255] = {0}; - char *shader_basename = - basename_without_suffix(state->screen_shader.filename); - snprintf(image_filename, sizeof(image_filename), - "%s_%zu_%d%02d%02d_%02d%02d%02d.png", shader_basename, - state->frame_count, timenow->tm_year + 1900, timenow->tm_mon, - timenow->tm_mday, timenow->tm_hour, timenow->tm_min, - timenow->tm_sec); - - int viewport[4] = {0}; - glGetIntegerv(GL_VIEWPORT, viewport); - - GLubyte *pixels = calloc(3 * viewport[2] * viewport[3], 1); - glReadPixels(0, 0, viewport[2], viewport[3], GL_BGR, GL_UNSIGNED_BYTE, - pixels); - - FIBITMAP *image = FreeImage_ConvertFromRawBits( - pixels, viewport[2], viewport[3], 3 * viewport[2], 24, FI_RGBA_RED_MASK, - FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, false); - - if (FreeImage_Save(FIF_PNG, image, image_filename, 0)) { - log_debug("Image saved to %s", image_filename); - } else { - log_error("Failed to saved image to %s", image_filename); - } - - FreeImage_Unload(image); - free(pixels); -} - -/** - * @brief Ensure the window is closed when the user presses the escape - * key. - * - * @param state The current state of the renderer. - */ -void process_input(struct renderer_state *state) { - bool should_reload = false; - - // Skip inotify checking if it's not available - if (state->inotify_fd != -1 && - (state->screen_shader.wd != -1 || state->buffer_shader.wd != -1)) { - char buf[BUF_LEN] = {0}; - int num_read = read(state->inotify_fd, buf, BUF_LEN); - if (num_read == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) { - // No event, do nothing - } else if (num_read <= 0) { - log_error("[inotify] Could not read inotify state"); - } else { - should_reload = true; - } - } - - if (glfwGetKey(state->window, GLFW_KEY_ESCAPE) == GLFW_PRESS) { - log_info("Quitting"); - glfwSetWindowShouldClose(state->window, true); - } else if (should_reload || - glfwGetKey(state->window, GLFW_KEY_R) == GLFW_PRESS) { - if (should_reload) { - log_info("File changed on disk, reloading shaders"); - } else { - log_info("Reloading shaders"); - } - // reinitialize time and frame count - state->frame_count = 0; - state->prev_frame_count = 0; - glfwSetTime(0.0); - state->time = 0.0; - state->prev_time = 0.0; - // recompile shaders - compile_shaders(&state->screen_shader.program, - state->screen_shader.filename); - if (state->buffer_shader.filename) { - compile_shaders(&state->buffer_shader.program, - state->buffer_shader.filename); - } - } else if (glfwGetKey(state->window, GLFW_KEY_S) == GLFW_PRESS) { - capture_screenshot(state); - } -} diff --git a/src/renderer.h b/src/renderer.h index d694be4..e5fe9a5 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -34,8 +34,5 @@ unsigned int initialize_framebuffer(unsigned int *framebuffer, unsigned int texture_width, unsigned int texture_height); void framebuffer_size_callback(GLFWwindow *window, int width, int height); -char *basename_without_suffix(const char *filename); -void capture_screenshot(struct renderer_state *state); -void process_input(struct renderer_state *state); #endif /* RENDERER_H */