From f70ea1c21595376ea8bc02d80140721eaf627520 Mon Sep 17 00:00:00 2001 From: frank <420@shampoo.ooo> Date: Sat, 2 Oct 2021 19:21:07 -0400 Subject: [PATCH] started log class --- src/Configuration.cpp | 2 +- src/Configuration.hpp | 1 + src/Display.cpp | 9 +- src/Display.hpp | 10 +- src/GLObject.cpp | 15 +++ src/GLObject.hpp | 10 +- src/Game.cpp | 231 +++++++++++++++--------------------------- src/Game.hpp | 7 +- src/Log.cpp | 123 ++++++++++++++++++++++ src/Log.hpp | 72 +++++++++++++ src/Node.cpp | 14 +-- src/Node.hpp | 3 +- src/Pixels.cpp | 10 +- src/Pixels.hpp | 1 + src/Recorder.cpp | 20 ++-- src/Recorder.hpp | 1 + src/Sprite.cpp | 4 +- src/Sprite.hpp | 1 + src/Texture.cpp | 6 +- src/Texture.hpp | 3 +- src/extension.cpp | 54 ++++------ src/extension.hpp | 5 +- 22 files changed, 362 insertions(+), 240 deletions(-) create mode 100644 src/Log.cpp create mode 100644 src/Log.hpp diff --git a/src/Configuration.cpp b/src/Configuration.cpp index 29429d0..487a396 100644 --- a/src/Configuration.cpp +++ b/src/Configuration.cpp @@ -150,7 +150,7 @@ void Configuration::refresh() { std::ostringstream message; message << "config file modified, reloading " << config_path; - debug(message.str()); + sb::Log::log(message, sb::Log::DEBUG); load(); } } diff --git a/src/Configuration.hpp b/src/Configuration.hpp index fc43b43..3e0609c 100644 --- a/src/Configuration.hpp +++ b/src/Configuration.hpp @@ -5,6 +5,7 @@ #include "filesystem.hpp" #include "Node.hpp" #include "Animation.hpp" +#include "Log.hpp" class Configuration : public Node { diff --git a/src/Display.cpp b/src/Display.cpp index b0981b7..f3388d1 100644 --- a/src/Display.cpp +++ b/src/Display.cpp @@ -1,5 +1,4 @@ #include "Display.hpp" -#include "Game.hpp" /* Create a Display instance and subscribe to commands */ Display::Display(Node* parent) : Node(parent) @@ -28,7 +27,9 @@ Uint32 Display::pixel_format(int display_index) const SDL_DisplayMode display_mode; if (SDL_GetCurrentDisplayMode(display_index, &display_mode) != 0) { - SDL_Log("could not get display mode for index %i: %s", display_index, SDL_GetError()); + std::ostringstream message; + message << "could not get display mode for index " << display_index; + sb::Log::sdl_error(message.str()); return SDL_PIXELFORMAT_UNKNOWN; } else @@ -123,12 +124,12 @@ void Display::toggle_fullscreen() const SDL_Window* window = const_cast(get_window()); if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) { - log("fullscreen requested"); + sb::Log::log("fullscreen requested"); SDL_SetWindowFullscreen(window, 0); } else { - log("exit fullscreen requested"); + sb::Log::log("exit fullscreen requested"); SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN); } } diff --git a/src/Display.hpp b/src/Display.hpp index c521673..8065379 100644 --- a/src/Display.hpp +++ b/src/Display.hpp @@ -1,17 +1,15 @@ #ifndef Display_h_ #define Display_h_ -#define GLM_ENABLE_EXPERIMENTAL +#include #include "glm/vec2.hpp" - #include "SDL.h" - -#include +#include "SDL_image.h" #include "sdl2-gfx/SDL2_gfxPrimitives.h" #include "sdl2-gfx/SDL2_rotozoom.h" - #include "Node.hpp" #include "Box.hpp" +#include "Log.hpp" class Display : public Node { @@ -32,4 +30,6 @@ public: }; +#include "Game.hpp" + #endif diff --git a/src/GLObject.cpp b/src/GLObject.cpp index 41b05d4..acf0622 100644 --- a/src/GLObject.cpp +++ b/src/GLObject.cpp @@ -1,3 +1,15 @@ +/* /\ +--------------------------------------------------------------+ + ____/ \____ /| - zlib/MIT/Unlicenced game framework licensed to freely use, | + \ / / | copy, modify and sell without restriction | + +--\ ^__^ /--+ | | + | ~/ \~ | | - originally created at [http://nugget.fun] | + | ~~~~~~~~~~~~ | +--------------------------------------------------------------+ + | SPACE ~~~~~ | / + | ~~~~~~~ BOX |/ + +--------------+ + +*/ + #include "GLObject.hpp" /* The deleter function is used for freeing the memory allocated to the object (for example, glDeleteTextures, @@ -12,6 +24,9 @@ void GLObject::generate(generator_function generator) GLuint id; generator(1, &id); this->id(id); + std::ostringstream message; + message << "Generated ID " << this->id() << " for GL object"; + sb::Log::log(message, sb::Log::DEBUG); } /* Set the shared pointer to point to a new GLuint with specified ID value */ diff --git a/src/GLObject.hpp b/src/GLObject.hpp index a314ec9..3c30007 100644 --- a/src/GLObject.hpp +++ b/src/GLObject.hpp @@ -23,10 +23,6 @@ #ifndef GLObject_h_ #define GLObject_h_ -#include -#include -#include - /* include Open GL */ #if defined(__EMSCRIPTEN__) #include @@ -34,6 +30,12 @@ #include "glew/glew.h" #endif +#include +#include +#include +#include +#include "Log.hpp" + class GLObject { diff --git a/src/Game.cpp b/src/Game.cpp index 803bab5..3548e15 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -1,44 +1,5 @@ #include "Game.hpp" -FramerateIndicator::FramerateIndicator(Node* parent) : Sprite(parent) -{ - get_delegate().subscribe(&FramerateIndicator::respond, this); - hide(); -} - -void FramerateIndicator::respond(SDL_Event& event) -{ - if (get_delegate().compare(event, "toggle-framerate")) - { - toggle_hidden(); - } -} - -SDL_Surface* FramerateIndicator::get_surface() -{ - std::string padded = sb::pad(get_root()->frame_count_this_second, 2); - SDL_Surface* shaded = TTF_RenderText_Shaded( - get_root()->bp_mono_font, padded.c_str(), {0, 0, 0, 255}, {255, 255, 255, 255}); - if (!shaded) - { - get_root()->print_sdl_error("Could not create text"); - } - return shaded; -} - -void FramerateIndicator::refresh() -{ - if (!is_hidden() && get_root()->bp_mono_font != nullptr) - { - unload(); - SDL_Surface* surface = get_surface(); - SDL_Texture* texture = SDL_CreateTextureFromSurface(get_root()->get_renderer(), surface); - add_frames(texture); - SDL_FreeSurface(surface); - set_ne(get_display().window_box().ne()); - } -} - Game::Game() { /* Set the appropriate priority level for the default log category so either info level messages @@ -52,13 +13,13 @@ Game::Game() { default_log_category_priority = SDL_LOG_PRIORITY_INFO; } - SDL_LogSetPriority(DEFAULT_SDL_LOG_CATEGORY, default_log_category_priority); + SDL_LogSetPriority(sb::Log::DEFAULT_CATEGORY, default_log_category_priority); /* set custom log function that prints to stdout/stderr and to file if enabled */ SDL_LogSetOutputFunction(&Game::sdl_log_override, this); /* pretty print config to debug log */ std::ostringstream log_message; log_message << std::setw(4) << get_configuration() << std::endl; - debug(log_message.str()); + sb::Log::log(log_message, sb::Log::DEBUG); /* tell SDL which render driver you will be requesting when calling SDL_CreateRenderer */ SDL_SetHint(SDL_HINT_RENDER_DRIVER, get_configuration()["display"]["render driver"].get().c_str()); /* initialize the buffer of frame lengths which will be used to calculate FPS */ @@ -77,17 +38,17 @@ Game::Game() SDL_GetVersion(&version); log_message << "linked to SDL " << static_cast(version.major) << "." << static_cast(version.minor) << "." << static_cast(version.patch); - log(log_message.str()); + sb::Log::log(log_message); /* allows use of our own main function (?) see SDL_SetMainReady.html */ SDL_SetMainReady(); if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) { - print_sdl_error("SDL could not initialize"); + sb::Log::sdl_error("SDL could not initialize"); flag_to_end(); } log_message = std::ostringstream(); log_message << "GLEW " << glewGetString(GLEW_VERSION); - log(log_message.str()); + sb::Log::log(log_message.str()); glm::ivec2 window_size = get_configuration()["display"]["dimensions"].get(); /* Create a window with dimensions set in the config, centered, and flagged to be usable in OpenGL context */ window = SDL_CreateWindow( @@ -95,7 +56,7 @@ Game::Game() SDL_WINDOWPOS_CENTERED, window_size.x, window_size.y, SDL_WINDOW_OPENGL); if (window == nullptr) { - print_sdl_error("Could not create window"); + sb::Log::sdl_error("Could not create window"); flag_to_end(); } /* Create an SDL renderer for clearing the screen to black and for logging renderer properties. Destroy renderer @@ -103,7 +64,7 @@ Game::Game() */ if ((renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_TARGETTEXTURE | SDL_RENDERER_ACCELERATED)) == nullptr) { - print_sdl_error("Could not create renderer"); + sb::Log::sdl_error("Could not create renderer"); flag_to_end(); } else @@ -113,7 +74,7 @@ Game::Game() SDL_GetRendererOutputSize(renderer, &w, &h); log_message = std::ostringstream(); log_message << "renderer output size is " << w << "x" << h; - log(log_message.str()); + sb::Log::log(log_message); /* clear screen to black */ SDL_SetRenderTarget(renderer, nullptr); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); @@ -125,7 +86,7 @@ Game::Game() SDL_ShowCursor(get_configuration()["display"]["show-cursor"]); if (TTF_Init() < 0) { - print_sdl_error("Could not initialize SDL ttf"); + sb::Log::sdl_error("Could not initialize SDL ttf"); flag_to_end(); } else @@ -135,11 +96,11 @@ Game::Game() } if ((bp_mono_font = TTF_OpenFont("BPmono.ttf", 14)) == nullptr) { - print_error("Could not load BPmono.ttf"); + sb::Log::log("Could not load BPmono.ttf", sb::Log::ERROR); } if (Mix_Init(MIX_INIT_OGG) == 0) { - print_sdl_error("Could not initialize SDL mixer"); + sb::Log::sdl_error("Could not initialize SDL mixer"); flag_to_end(); } else @@ -151,20 +112,22 @@ Game::Game() // MIX_DEFAULT_CHANNELS, 1024) < 0) if (Mix_OpenAudio(11025, AUDIO_U8, MIX_DEFAULT_CHANNELS, 2048) < 0) { - print_sdl_error("Could not set up audio"); + sb::Log::sdl_error("Could not set up audio"); } SDL_Log("Using audio driver: %s", SDL_GetCurrentAudioDriver()); const int audio_device_count = SDL_GetNumAudioDevices(SDL_TRUE); for (int ii = 0; ii < audio_device_count; ii++) { - SDL_Log("Found audio capture device %i: %s", ii, SDL_GetAudioDeviceName(ii, SDL_TRUE)); + std::ostringstream message; + message << "Found audio capture device " << ii << ": " << SDL_GetAudioDeviceName(ii, SDL_TRUE); + sb::Log::log(message); } audio.load_sfx(); audio.load_bgm(); #if SDL_BYTEORDER == SDL_BIG_ENDIAN - log("big endian"); + sb::Log::log("big endian"); #else - log("little endian"); + sb::Log::log("little endian"); #endif last_frame_timestamp = SDL_GetTicks(); } @@ -186,7 +149,7 @@ void Game::load_sdl_context() } if ((renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_TARGETTEXTURE | SDL_RENDERER_ACCELERATED)) == nullptr) { - print_sdl_error("Could not create renderer"); + sb::Log::sdl_error("Could not create renderer"); flag_to_end(); } else @@ -216,46 +179,45 @@ void Game::load_gl_context() SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); if ((glcontext = SDL_GL_CreateContext(window)) == nullptr) { - print_sdl_error("Could not get GL context"); + sb::Log::sdl_error("Could not get GL context"); flag_to_end(); } /* try enabling vsync */ if (SDL_GL_SetSwapInterval(1) == 0) { - log("enabled vsync"); + sb::Log::log("enabled vsync"); } else { - log("vsync not supported"); + sb::Log::log("vsync not supported"); } GLenum error = glewInit(); std::ostringstream message; if (error != GLEW_OK) { message << "GLEW could not initialize " << glewGetErrorString(error); - print_error(message.str()); + sb::Log::log(message, sb::Log::ERROR); } message << "OpenGL " << glGetString(GL_VERSION) << ", renderer " << glGetString(GL_RENDERER) << ", shading language " << glGetString(GL_SHADING_LANGUAGE_VERSION); - log(message.str()); + sb::Log::log(message); is_gl_context = true; log_display_mode(); } /* Overrides SDL's default log function to log a message to stdout/stderr and, if log is enabled in the * global configuration, to a file. Debug level statements may be suppressed, printed to stdout, or printed to - * both stdout and file, depending on the global configuration. - */ + * both stdout and file, depending on the global configuration. */ void Game::sdl_log_override(void* userdata, int category, SDL_LogPriority priority, const char* message) { Game* game = static_cast(userdata); std::ostream& out = (priority > SDL_LOG_PRIORITY_WARN) ? std::cerr : std::cout; - // print to stdout/stderr if priority is higher than debug or debug statements are enabled + /* print to stdout/stderr if priority is higher than debug or debug statements are enabled */ if (priority > SDL_LOG_PRIORITY_DEBUG || game->get_configuration()["log"]["debug-to-stdout"]) { out << message << std::endl; } - // handle writing to log file + /* handle writing to log file */ if (game->get_configuration()["log"]["enabled"]) { fs::path path = game->get_configuration()["log"]["output-directory"]; @@ -263,18 +225,18 @@ void Game::sdl_log_override(void* userdata, int category, SDL_LogPriority priori { fs::create_directories(path); } - // prepend a timestamp to the message + /* prepend a timestamp to the message */ std::time_t now = std::time(nullptr); std::stringstream stamped_message; stamped_message << std::put_time(std::localtime(&now), "%F %T ") << message; - // if debug is enabled, append message to debug log file + /* if debug is enabled, append message to debug log file */ if (game->get_configuration()["log"]["debug-to-file"]) { fs::path debug_path = path / game->get_configuration()["log"]["debug-file-name"]; std::ofstream debug_stream(debug_path, std::ios_base::app); debug_stream << stamped_message.str() << std::endl; } - // only append messages to the info log that are higher than debug priority + /* only append messages to the info log that are higher than debug priority */ if (priority > SDL_LOG_PRIORITY_DEBUG) { fs::path info_path = path / game->get_configuration()["log"]["info-file-name"]; @@ -284,16 +246,6 @@ void Game::sdl_log_override(void* userdata, int category, SDL_LogPriority priori } } -void Game::print_error(const std::string& message) -{ - sb::print_error(message); -} - -void Game::print_sdl_error(const std::string& message) -{ - sb::print_sdl_error(message); -} - void Game::print_frame_length_history() { for (float& frame_length : frame_length_history) @@ -318,7 +270,7 @@ GLuint Game::load_shader(const fs::path& path, GLenum type) const if (is_compiled == GL_TRUE) { message << "compiled shader at " << path; - log(message.str()); + sb::Log::log(message); return shader; } else @@ -330,7 +282,7 @@ GLuint Game::load_shader(const fs::path& path, GLenum type) const error_info.resize(max_length, 0); glGetShaderInfoLog(shader, error_info.size(), nullptr, error_info.data()); message << "failed to compile " << path << ": " << error_info; - log(message.str()); + sb::Log::log(message, sb::Log::Level::ERROR); return -1; } } @@ -344,7 +296,7 @@ bool Game::link_shader(GLuint program) const if (is_linked == GL_TRUE) { message << "linked shader program " << program; - log(message.str()); + sb::Log::log(message); return true; } else @@ -356,81 +308,18 @@ bool Game::link_shader(GLuint program) const error_info.resize(max_length, 0); glGetProgramInfoLog(program, error_info.size(), nullptr, error_info.data()); message << "failed linking shader program " << program << ": " << error_info; - log(message.str()); + sb::Log::log(message, sb::Log::Level::ERROR); return false; } } -/* Send an info priority message to SDL's log output function, which is overridden by our own member - * function. Category will default to SDL's custom category. - */ -void Game::log(const std::string& message, const int category) -{ - SDL_LogInfo(category, "%s", message.c_str()); -} - -/* Send a debug priority message to SDL's log output function, which is overridden by our own member - * function. Category will default to SDL's custom category. Using the default category will ensure - * that debug level statements are handled according to the options in the global configuration. - */ -void Game::debug(const std::string& message, const int category) -{ - SDL_LogDebug(category, "%s", message.c_str()); -} - void Game::log_renderer_info(SDL_RendererInfo& info) { - SDL_Log("renderer name: %s, flags: %i, texture formats: %i, " - "max texture w: %i, max texture h: %i", info.name, info.flags, - info.num_texture_formats, info.max_texture_width, - info.max_texture_height); -} - -bool Game::log_gl_errors(std::string suffix) -{ - GLenum error; - bool error_logged = false; - while ((error = glGetError()) != GL_NO_ERROR) - { - error_logged = true; - std::ostringstream message; - if (error == GL_INVALID_ENUM) - { - message << "GL_INVALID_ENUM, an unacceptable value is specified for an enumerated argument"; - } - else if (error == GL_INVALID_VALUE) - { - message << "GL_INVALID_VALUE, a numeric argument is out of range"; - } - else if (error == GL_INVALID_OPERATION) - { - message << "GL_INVALID_OPERATION, the specified operation is not allowed in the current state"; - } - else if (error == GL_INVALID_FRAMEBUFFER_OPERATION) - { - message << "GL_INVALID_FRAMEBUFFER_OPERATION, the framebuffer object is not complete"; - } - else if (error == GL_OUT_OF_MEMORY) - { - message << "GL_OUT_OF_MEMORY, there is not enough memory left to execute the command"; - } - else if (error == GL_STACK_UNDERFLOW) - { - message << "GL_STACK_UNDERFLOW, an attempt has been made to perform an operation that would " << - "cause an internal stack to underflow"; - } - else if (error == GL_STACK_OVERFLOW) - { - message << "GL_STACK_OVERFLOW, an attempt has been made to perform an operation that would " << - "cause an internal stack to overflow"; - } - if (!suffix.empty()) - { - message << " " << suffix; - } - log(message.str()); - } - return error_logged; + std::ostringstream message; + message << "renderer name: " << info.name << ", flags: " << info.flags << ", texture formats: " << + info.num_texture_formats << ", max texture w: " << info.max_texture_width << ", max texture h: " << + info.max_texture_height; + sb::Log::log(message); } /* Write resolution, monitor refresh rate, and pixel format to the log. Code taken from SDL_GetCurrentDisplayMode.html @@ -444,14 +333,15 @@ void Game::log_display_mode() int mode = SDL_GetCurrentDisplayMode(ii, ¤t); if (mode != 0) { - message << "Could not get display mode for video display #" << ii << ": " << SDL_GetError(); + message << "Could not get display mode for video display #" << ii; + sb::Log::sdl_error(message.str()); } else { message << "Display #" << ii << ": display mode is " << current.w << "x" << current.h << "px @ " << current.refresh_rate << "hz " << get_pixel_format_string(current.format); + sb::Log::log(message); } - log(message.str()); } } @@ -772,3 +662,42 @@ Game::~Game() { get_delegate().unsubscribe(this); } + +FramerateIndicator::FramerateIndicator(Node* parent) : Sprite(parent) +{ + get_delegate().subscribe(&FramerateIndicator::respond, this); + hide(); +} + +void FramerateIndicator::respond(SDL_Event& event) +{ + if (get_delegate().compare(event, "toggle-framerate")) + { + toggle_hidden(); + } +} + +SDL_Surface* FramerateIndicator::get_surface() +{ + std::string padded = sb::pad(get_root()->frame_count_this_second, 2); + SDL_Surface* shaded = TTF_RenderText_Shaded( + get_root()->bp_mono_font, padded.c_str(), {0, 0, 0, 255}, {255, 255, 255, 255}); + if (!shaded) + { + sb::Log::sdl_error("Could not create text"); + } + return shaded; +} + +void FramerateIndicator::refresh() +{ + if (!is_hidden() && get_root()->bp_mono_font != nullptr) + { + unload(); + SDL_Surface* surface = get_surface(); + SDL_Texture* texture = SDL_CreateTextureFromSurface(get_root()->get_renderer(), surface); + add_frames(texture); + SDL_FreeSurface(surface); + set_ne(get_display().window_box().ne()); + } +} diff --git a/src/Game.hpp b/src/Game.hpp index ae30958..d91d179 100644 --- a/src/Game.hpp +++ b/src/Game.hpp @@ -32,6 +32,7 @@ #include "Recorder.hpp" #include "Sprite.hpp" #include "Audio.hpp" +#include "Log.hpp" #include "filesystem.hpp" #include "extension.hpp" @@ -71,7 +72,6 @@ public: Game(Game&&) = delete; Game& operator=(Game&&) = delete; - static const int DEFAULT_SDL_LOG_CATEGORY = SDL_LOG_CATEGORY_CUSTOM; SDL_Window* window; SDL_Renderer* renderer = nullptr; SDL_GLContext glcontext = nullptr; @@ -90,17 +90,12 @@ public: Game(); virtual void reset() { activate(); } - void print_error(const std::string&); - void print_sdl_error(const std::string&); void print_frame_length_history(); void load_sdl_context(); void load_gl_context(); GLuint load_shader(const fs::path&, GLenum) const; bool link_shader(GLuint program) const; - static void log(const std::string&, const int = DEFAULT_SDL_LOG_CATEGORY); - static void debug(const std::string&, const int = DEFAULT_SDL_LOG_CATEGORY); void log_renderer_info(SDL_RendererInfo&); - static bool log_gl_errors(std::string = ""); void log_display_mode(); void log_surface_format(SDL_Surface*, std::string = "surface"); std::string get_pixel_format_string(Uint32); diff --git a/src/Log.cpp b/src/Log.cpp new file mode 100644 index 0000000..0d6e7a2 --- /dev/null +++ b/src/Log.cpp @@ -0,0 +1,123 @@ +/* /\ +--------------------------------------------------------------+ + ____/ \____ /| - zlib/MIT/Unlicenced game framework licensed to freely use, | + \ / / | copy, modify and sell without restriction | + +--\ ^__^ /--+ | | + | ~/ \~ | | - originally created at [http://nugget.fun] | + | ~~~~~~~~~~~~ | +--------------------------------------------------------------+ + | SPACE ~~~~~ | / + | ~~~~~~~ BOX |/ + +--------------+ */ + +#include "Log.hpp" + +/* Send a message to SDL's log function, which currently gets overridden in the Game class. + * The default level is INFO. Category will default to SDL's custom category. Using the default + * category will ensure that debug level statements are handled according to the options in the + * global configuration. */ +void sb::Log::log(const std::string& message, const Level level, const int category) +{ + SDL_LogMessage(category, static_cast(level), "%s", message.c_str()); +} + +/* Convert string stream to string and forward */ +void sb::Log::log(const std::ostringstream& message, const Level level, const int category) +{ + log(message.str(), level, category); +} + +/* Log all GL errors accumulated since the last time this function was called */ +bool sb::Log::gl_errors(const std::string& suffix) +{ + GLenum error; + bool error_logged = false; + while ((error = glGetError()) != GL_NO_ERROR) + { + error_logged = true; + std::ostringstream message; + if (error == GL_INVALID_ENUM) + { + message << "GL_INVALID_ENUM, an unacceptable value is specified for an enumerated argument"; + } + else if (error == GL_INVALID_VALUE) + { + message << "GL_INVALID_VALUE, a numeric argument is out of range"; + } + else if (error == GL_INVALID_OPERATION) + { + message << "GL_INVALID_OPERATION, the specified operation is not allowed in the current state"; + } + else if (error == GL_INVALID_FRAMEBUFFER_OPERATION) + { + message << "GL_INVALID_FRAMEBUFFER_OPERATION, the framebuffer object is not complete"; + } + else if (error == GL_OUT_OF_MEMORY) + { + message << "GL_OUT_OF_MEMORY, there is not enough memory left to execute the command"; + } + else if (error == GL_STACK_UNDERFLOW) + { + message << "GL_STACK_UNDERFLOW, an attempt has been made to perform an operation that would " << + "cause an internal stack to underflow"; + } + else if (error == GL_STACK_OVERFLOW) + { + message << "GL_STACK_OVERFLOW, an attempt has been made to perform an operation that would " << + "cause an internal stack to overflow"; + } + if (!suffix.empty()) + { + message << " " << suffix; + } + log(message); + } + return error_logged; +} + +void sb::Log::sdl_error(const std::string& original_message) +{ + std::ostringstream message; + message << original_message << " " << SDL_GetError(); + log(message, Level::ERROR); +} + +/* Overrides SDL's default log function to log a message to stdout/stderr and, if log is enabled in the + * global configuration, to a file. Debug level statements may be suppressed, printed to stdout, or printed to + * both stdout and file, depending on the global configuration. + */ +// void sb::Log::record(void* userdata, int category, SDL_LogPriority priority, const char* message) +// { +// Game* game = static_cast(userdata); +// std::ostream& out = (priority > SDL_LOG_PRIORITY_WARN) ? std::cerr : std::cout; +// /* print to stdout/stderr if priority is higher than debug or debug statements are enabled */ +// if (priority > SDL_LOG_PRIORITY_DEBUG /* || game->get_configuration()["log"]["debug-to-stdout"] */) +// { +// out << message << std::endl; +// } +// /* handle writing to log file */ +// if (game->get_configuration()["log"]["enabled"]) +// { +// fs::path path = game->get_configuration()["log"]["output-directory"]; +// if (!fs::exists(path)) +// { +// fs::create_directories(path); +// } +// /* prepend a timestamp to the message */ +// std::time_t now = std::time(nullptr); +// std::stringstream stamped_message; +// stamped_message << std::put_time(std::localtime(&now), "%F %T ") << message; +// /* if debug is enabled, append message to debug log file */ +// if (game->get_configuration()["log"]["debug-to-file"]) +// { +// fs::path debug_path = path / game->get_configuration()["log"]["debug-file-name"]; +// std::ofstream debug_stream(debug_path, std::ios_base::app); +// debug_stream << stamped_message.str() << std::endl; +// } +// /* only append messages to the info log that are higher than debug priority */ +// if (priority > SDL_LOG_PRIORITY_DEBUG) +// { +// fs::path info_path = path / game->get_configuration()["log"]["info-file-name"]; +// std::ofstream info_stream(info_path, std::ios_base::app); +// info_stream << stamped_message.str() << std::endl; +// } +// } +// } diff --git a/src/Log.hpp b/src/Log.hpp new file mode 100644 index 0000000..b6caab9 --- /dev/null +++ b/src/Log.hpp @@ -0,0 +1,72 @@ +/* /\ +--------------------------------------------------------------+ + ____/ \____ /| - zlib/MIT/Unlicenced game framework licensed to freely use, | + \ / / | copy, modify and sell without restriction | + +--\ ^__^ /--+ | | + | ~/ \~ | | - originally created at [http://nugget.fun] | + | ~~~~~~~~~~~~ | +--------------------------------------------------------------+ + | SPACE ~~~~~ | / + | ~~~~~~~ BOX |/ + +--------------+ + + [Log.hpp] + + Log messages of specified priority to the SDL logging method, which is overridden in + the Game class to write to stdout, file, or both, depending on the user's configuration + settings. + + The logging methods can be used statically, or the log object defined here can be used + like a stream to stream messages to. + +*/ + +#ifndef Log_h_ +#define Log_h_ + +/* include Open GL */ +#if defined(__EMSCRIPTEN__) +#include +#else +#include "glew/glew.h" +#endif + +#include +#include +#include +#include +/* #include "filesystem.hpp" */ + +namespace sb +{ + + class Log + { + + public: + + /* These definitions are equivalent to SDL's SDL_LOG_PRIORITY_* values */ + enum Level + { + VERBOSE = 1, + DEBUG, + INFO, + WARN, + ERROR, + CRITICAL, + }; + + static const int DEFAULT_CATEGORY = SDL_LOG_CATEGORY_CUSTOM; + + Log(std::function); + static void log(const std::string&, const Level = INFO, const int = DEFAULT_CATEGORY); + static void log(const std::ostringstream&, const Level = INFO, const int = DEFAULT_CATEGORY); + static bool gl_errors(const std::string& = ""); + static void sdl_error(const std::string&); + /* static void record(void*, int, SDL_LogPriority, const char*); */ + + }; + + /* Log log = Log(&Log::record); */ + +} + +#endif diff --git a/src/Node.cpp b/src/Node.cpp index 167fc24..c181a7e 100644 --- a/src/Node.cpp +++ b/src/Node.cpp @@ -11,7 +11,7 @@ Node::Node() : Node(nullptr) {} Node::Node(Node* parent) : parent(parent) { - debug("constructing node " + get_branch_as_string()); + sb::Log::log("constructing node " + get_branch_as_string(), sb::Log::DEBUG); } void Node::set_parent(Node* other) @@ -124,16 +124,6 @@ void Node::unsuppress_input() get_root()->get_input().unsuppress(); } -void Node::log(const std::string& message) const -{ - get_root()->log(message); -} - -void Node::debug(const std::string& message) const -{ - get_root()->debug(message); -} - const std::string Node::get_branch_as_string() const { const Node* current = this; @@ -152,6 +142,6 @@ const std::string Node::get_branch_as_string() const Node::~Node() { - debug("destroying node " + get_branch_as_string()); + sb::Log::log("destroying node " + get_branch_as_string(), sb::Log::DEBUG); get_delegate().unsubscribe(this); } diff --git a/src/Node.hpp b/src/Node.hpp index 67b6dff..df99aa4 100644 --- a/src/Node.hpp +++ b/src/Node.hpp @@ -6,6 +6,7 @@ #include "glm/vec2.hpp" #include "json/json.hpp" #include "SDL.h" +#include "Log.hpp" #include "filesystem.hpp" class Game; @@ -45,8 +46,6 @@ public: void suppress_input(); void suppress_input_temporarily(int = 0); void unsuppress_input(); - void log(const std::string&) const; - void debug(const std::string&) const; const std::string get_branch_as_string() const; virtual std::string class_name() const { return "Node"; }; virtual ~Node(); diff --git a/src/Pixels.cpp b/src/Pixels.cpp index af66605..f6235fb 100644 --- a/src/Pixels.cpp +++ b/src/Pixels.cpp @@ -11,11 +11,11 @@ Pixels::Pixels(SDL_Renderer* renderer, SDL_Texture* texture, const Box& box) : SDL_DisplayMode display_mode; if (SDL_GetCurrentDisplayMode(0, &display_mode) < 0) { - sb::print_sdl_error("could not get current display mode"); + sb::Log::sdl_error("could not get current display mode"); } if (SDL_GetRendererOutputSize(renderer, &w, &h) < 0) { - sb::print_sdl_error("could not get renderer output size"); + sb::Log::sdl_error("could not get renderer output size"); } format_enum = display_mode.format; texture_access = TEXTURE_ACCESS_SCREEN; @@ -57,7 +57,7 @@ Pixels::Pixels(SDL_Renderer* renderer, SDL_Texture* texture, const Box& box) : source = operator new(bytes_total); if (SDL_RenderReadPixels(renderer, &rect, format->format, source, format->BytesPerPixel * rect.w) < 0) { - sb::print_sdl_error("could not read pixels"); + sb::Log::sdl_error("could not read pixels"); operator delete(source); } else @@ -74,12 +74,12 @@ Pixels::Pixels(SDL_Renderer* renderer, SDL_Texture* texture, const Box& box) : int pitch; if (SDL_LockTexture(texture, &rect, &source, &pitch) < 0) { - sb::print_sdl_error("could not lock texture"); + sb::Log::sdl_error("could not lock texture"); } } else { - sb::print_error("unknown texture format for loading pixels"); + sb::Log::log("unknown texture format for loading pixels", sb::Log::Level::ERROR); } } diff --git a/src/Pixels.hpp b/src/Pixels.hpp index 9257395..8f582c2 100644 --- a/src/Pixels.hpp +++ b/src/Pixels.hpp @@ -4,6 +4,7 @@ #include "SDL.h" #include "Box.hpp" #include "Color.hpp" +#include "Log.hpp" #include "extension.hpp" class Sprite; diff --git a/src/Recorder.cpp b/src/Recorder.cpp index e3ef537..206c4b8 100644 --- a/src/Recorder.cpp +++ b/src/Recorder.cpp @@ -43,7 +43,7 @@ void Recorder::respond(SDL_Event& event) } else { - SDL_Log("Writing in progress, cannot start recording"); + sb::Log::log("Writing in progress, cannot start recording"); } } else if (get_delegate().compare(event, "save-current-stash")) @@ -52,7 +52,9 @@ void Recorder::respond(SDL_Event& event) } else if (get_delegate().compare(event, "print-video-memory-size")) { - SDL_Log("Video memory size is %iMB", get_memory_size()); + std::ostringstream message; + message << "Video memory size is " << get_memory_size() << "MB"; + sb::Log::log(message); } } @@ -74,7 +76,7 @@ void Recorder::capture_screen() SDL_FreeSurface(surface); std::ostringstream message; message << "saved screenshot to " << path; - log(message.str()); + sb::Log::log(message); } /* Writes a video of what was just displayed on the screen up until the function was called. The length @@ -87,7 +89,7 @@ void Recorder::grab_stash() int length = get_configuration()["recording"]["max-stash-length"]; std::ostringstream message; message << "stashing most recent " << length / 1000.0f << " seconds of video"; - log(message.str()); + sb::Log::log(message); most_recent_stash = current_stash; current_stash = Stash(); writing_recording = true; @@ -97,7 +99,7 @@ void Recorder::grab_stash() } else { - log("recording in progress, cannot grab most recent frames"); + sb::Log::log("recording in progress, cannot grab most recent frames"); } } @@ -117,7 +119,9 @@ void Recorder::write_most_recent_frames() { write_mp4(); } - SDL_Log("wrote video frames to %s", current_video_directory.c_str()); + std::ostringstream message; + message << "wrote video frames to " << current_video_directory; + sb::Log::log(message); writing_recording = false; } @@ -125,7 +129,7 @@ void Recorder::start_recording() { if (!writing_recording) { - log("starting recording"); + sb::Log::log("starting recording"); is_recording = true; video_stashes.push_back(Stash()); make_directory(); @@ -133,7 +137,7 @@ void Recorder::start_recording() } else { - log("writing in progress, cannot start recording"); + sb::Log::log("writing in progress, cannot start recording", sb::Log::WARN); } } diff --git a/src/Recorder.hpp b/src/Recorder.hpp index 0d83b85..5741d62 100644 --- a/src/Recorder.hpp +++ b/src/Recorder.hpp @@ -18,6 +18,7 @@ #include "filesystem.hpp" #include "Node.hpp" #include "Animation.hpp" +#include "Log.hpp" struct Stash { diff --git a/src/Sprite.cpp b/src/Sprite.cpp index a711e86..61073f2 100644 --- a/src/Sprite.cpp +++ b/src/Sprite.cpp @@ -44,7 +44,7 @@ void Sprite::associate(std::string path) { std::ostringstream message; message << "invalid path " << path; - get_root()->print_error(message.str()); + sb::Log::log(message, sb::Log::Level::ERROR); } } @@ -78,7 +78,7 @@ void Sprite::load_file(fs::path path) SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, previous_scale_quality); if (not texture) { - game->print_sdl_error("Could not load image"); + sb::Log::sdl_error("Could not load image"); } else { diff --git a/src/Sprite.hpp b/src/Sprite.hpp index 9728132..29e00ad 100644 --- a/src/Sprite.hpp +++ b/src/Sprite.hpp @@ -15,6 +15,7 @@ #include "Box.hpp" #include "Animation.hpp" #include "Color.hpp" +#include "Log.hpp" class Game; diff --git a/src/Texture.cpp b/src/Texture.cpp index 983e05c..80d0b52 100644 --- a/src/Texture.cpp +++ b/src/Texture.cpp @@ -30,7 +30,7 @@ void Texture::generate(glm::vec2 size) glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGB8, size.x, size.y); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - Game::log_gl_errors(); + sb::Log::gl_errors(); } /* When called with no parameters, use the stored path variable */ @@ -65,7 +65,7 @@ void Texture::load(SDL_Surface* surface) load(surface->pixels, {surface->w, surface->h}, GL_RGBA, GL_UNSIGNED_BYTE); std::ostringstream message; message << "loaded " << path << " (" << surface->w << "x" << surface->h << ")"; - Game::log(message.str()); + sb::Log::log(message); } /* Bind texture and load pixel data using this class's generated ID */ @@ -77,7 +77,7 @@ void Texture::load(void* pixels, glm::vec2 size, GLenum format, GLenum type) } bind(); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size.x, size.y, format, type, pixels); - Game::log_gl_errors(); + sb::Log::gl_errors(); } /* The texture must have been previously generated with a size to use this generic pixel data load function */ diff --git a/src/Texture.hpp b/src/Texture.hpp index 1e7ead9..91d278f 100644 --- a/src/Texture.hpp +++ b/src/Texture.hpp @@ -23,12 +23,13 @@ #define Texture_h_ #include +#include "glm/vec2.hpp" #include "SDL.h" #include "SDL_image.h" #include "sdl2-gfx/SDL2_rotozoom.h" #include "filesystem.hpp" #include "GLObject.hpp" -#include "Game.hpp" +#include "Log.hpp" /* include Open GL */ #if defined(__EMSCRIPTEN__) diff --git a/src/extension.cpp b/src/extension.cpp index 08408c4..5a641af 100644 --- a/src/extension.cpp +++ b/src/extension.cpp @@ -99,7 +99,7 @@ void sb::populate_pixel_2d_array( int access; if (SDL_QueryTexture(texture, nullptr, &access, nullptr, nullptr) < 0) { - print_sdl_error("Could not query texture for access flag"); + sb::Log::sdl_error("Could not query texture for access flag"); } else { @@ -109,7 +109,7 @@ void sb::populate_pixel_2d_array( } if (SDL_SetRenderTarget(renderer, texture) < 0) { - print_sdl_error("Could not set render target"); + sb::Log::sdl_error("Could not set render target"); } else { @@ -121,7 +121,7 @@ void sb::populate_pixel_2d_array( SDL_Rect int_rect = region; if (SDL_RenderReadPixels(renderer, &int_rect, format, source, bytes_per_row) < 0) { - print_sdl_error("Could not read pixels after setting remapped texture as target"); + sb::Log::sdl_error("Could not read pixels after setting remapped texture as target"); } else { @@ -226,14 +226,14 @@ void sb::fill_texture(SDL_Renderer* renderer, SDL_Texture* texture, SDL_Texture* SDL_FRect draw_rect; if (SDL_SetRenderTarget(renderer, texture) < 0) { - print_sdl_error("could not set render target"); + sb::Log::sdl_error("could not set render target"); } else { SDL_Rect int_rect = box; if (SDL_RenderSetClipRect(renderer, &int_rect) < 0) { - print_sdl_error("could not set clip"); + sb::Log::sdl_error("could not set clip"); } else { @@ -260,7 +260,7 @@ SDL_Texture* sb::get_filled_texture(SDL_Renderer* renderer, glm::vec2 size, cons SDL_Texture* texture; if ((texture = SDL_CreateTexture(renderer, format, SDL_TEXTUREACCESS_TARGET, size.x, size.y)) == nullptr) { - print_sdl_error("could not create texture to fill"); + sb::Log::sdl_error("could not create texture to fill"); } else { @@ -274,7 +274,7 @@ SDL_Texture* sb::get_filled_texture(SDL_Renderer* renderer, glm::vec2 size, SDL_ SDL_Texture* texture; if ((texture = SDL_CreateTexture(renderer, format, SDL_TEXTUREACCESS_TARGET, size.x, size.y)) == nullptr) { - print_sdl_error("could not create texture to fill"); + sb::Log::sdl_error("could not create texture to fill"); } else { @@ -290,7 +290,7 @@ SDL_Texture* sb::get_hue_shifted_texture(SDL_Renderer* renderer, SDL_Texture* ba int w, h; if (SDL_QueryTexture(hue_shifted_texture, &pixel_format, nullptr, &w, &h) < 0) { - print_sdl_error("could not query texture"); + sb::Log::sdl_error("could not query texture"); } else { @@ -303,7 +303,7 @@ SDL_Texture* sb::get_hue_shifted_texture(SDL_Renderer* renderer, SDL_Texture* ba Uint32* pixels = new Uint32[length]; if (SDL_RenderReadPixels(renderer, NULL, pixel_format, pixels, bytes_per_row) < 0) { - print_sdl_error("Could not read pixels"); + sb::Log::sdl_error("Could not read pixels"); } else { @@ -317,7 +317,7 @@ SDL_Texture* sb::get_hue_shifted_texture(SDL_Renderer* renderer, SDL_Texture* ba } if (SDL_UpdateTexture(hue_shifted_texture, NULL, pixels, bytes_per_row) < 0) { - print_sdl_error("Could not apply hue shifted pixels update to texture"); + sb::Log::sdl_error("Could not apply hue shifted pixels update to texture"); } } delete[] pixels; @@ -341,19 +341,19 @@ SDL_Texture* sb::duplicate_texture(SDL_Renderer* renderer, SDL_Texture* base, co SDL_Texture* duplicate = SDL_CreateTexture(renderer, format, SDL_TEXTUREACCESS_TARGET, size.x, size.y); if (duplicate == NULL) { - print_sdl_error("could not create texture from base"); + sb::Log::sdl_error("could not create texture from base"); return NULL; } if ((SDL_SetRenderTarget(renderer, duplicate)) < 0) { - print_sdl_error("could not set render target to duplicate"); + sb::Log::sdl_error("could not set render target to duplicate"); return NULL; } SDL_SetTextureBlendMode(base, SDL_BLENDMODE_NONE); SDL_SetTextureBlendMode(duplicate, SDL_BLENDMODE_BLEND); if ((SDL_RenderCopyF(renderer, base, nullptr, nullptr)) < 0) { - print_sdl_error("could not render base onto duplicate"); + sb::Log::sdl_error("could not render base onto duplicate"); return nullptr; } SDL_SetTextureBlendMode(base, original_blend_mode); @@ -367,12 +367,12 @@ SDL_Texture* sb::get_remapped_texture( SDL_Texture* remapped = duplicate_texture(renderer, base); if (remapped == nullptr) { - print_sdl_error("could not duplicate base texture"); + sb::Log::sdl_error("could not duplicate base texture"); return nullptr; } if ((SDL_SetRenderTarget(renderer, remapped)) < 0) { - print_sdl_error("could not set render target to remapped texture"); + sb::Log::sdl_error("could not set render target to remapped texture"); return nullptr; } Pixels pixels = Pixels(renderer, remapped); @@ -399,13 +399,13 @@ SDL_Texture* sb::get_remapped_texture( SDL_Texture* base = IMG_LoadTexture(renderer, path.c_str()); if (base == nullptr) { - print_sdl_error("error loading file"); + sb::Log::sdl_error("error loading file"); return nullptr; } SDL_Texture* remapped = get_remapped_texture(renderer, base, map); if (remapped == nullptr) { - print_error("could not remap texture"); + sb::Log::log("could not remap texture", sb::Log::ERROR); return nullptr; } SDL_DestroyTexture(base); @@ -422,7 +422,7 @@ SDL_Texture* sb::get_pixel_scaled_texture(SDL_Renderer* renderer, SDL_Texture* b { if ((SDL_SetRenderTarget(renderer, base)) < 0) { - print_sdl_error("could not set render target to remapped texture"); + sb::Log::sdl_error("could not set render target to remapped texture"); return nullptr; } glm::ivec2 size = get_texture_box(base).size(); @@ -440,7 +440,7 @@ SDL_Texture* sb::get_pixel_scaled_texture(SDL_Renderer* renderer, SDL_Texture* b src_begin = src; if ((SDL_RenderReadPixels(renderer, NULL, format, src, bytes_per_row)) < 0) { - print_sdl_error("could not read pixels after setting remapped texture as target"); + sb::Log::sdl_error("could not read pixels after setting remapped texture as target"); return NULL; } } @@ -519,11 +519,11 @@ SDL_Texture* sb::get_pixel_scaled_texture(SDL_Renderer* renderer, SDL_Texture* b SDL_Texture* scaled = SDL_CreateTexture(renderer, format, SDL_TEXTUREACCESS_TARGET, size.x, size.y); if (scaled == nullptr) { - print_sdl_error("could not create scaled texture"); + sb::Log::sdl_error("could not create scaled texture"); } if (SDL_UpdateTexture(scaled, nullptr, dst_begin, bytes_per_row * 2) < 0) { - print_sdl_error("could not copy pixels to scaled texture"); + sb::Log::sdl_error("could not copy pixels to scaled texture"); } delete[] dst_begin; return scaled; @@ -557,7 +557,7 @@ SDL_Surface* sb::get_surface_from_pixels(Pixels& pixels) pixels.format->Amask); if (surface == nullptr) { - print_sdl_error("could not create RGB surface from texture pixel data"); + sb::Log::sdl_error("could not create RGB surface from texture pixel data"); } else { @@ -624,16 +624,6 @@ std::string sb::file_to_string(const fs::path& path) } } -void sb::print_error(const std::string& message) -{ - std::cerr << message << std::endl; -} - -void sb::print_sdl_error(const std::string& message) -{ - std::cerr << message << " " << SDL_GetError() << std::endl; -} - int SDL_SetRenderDrawColor(SDL_Renderer* renderer, const Color& color) { return SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a); diff --git a/src/extension.hpp b/src/extension.hpp index 68982ec..65a6b40 100644 --- a/src/extension.hpp +++ b/src/extension.hpp @@ -12,7 +12,6 @@ #include #include #include - #include "SDL.h" #include "SDL_image.h" #include "SDL_pixels.h" @@ -22,10 +21,10 @@ #include "glm/gtx/vector_angle.hpp" #include "json/json.hpp" #include "sdl2-gfx/SDL2_gfxPrimitives.h" - #include "Box.hpp" #include "Segment.hpp" #include "Color.hpp" +#include "Log.hpp" #include "filesystem.hpp" struct Pixels; @@ -67,8 +66,6 @@ namespace sb std::vector glob(fs::path); fs::path get_next_file_name(fs::path, int = 0, std::string = "", std::string = ""); std::string file_to_string(const fs::path&); - void print_error(const std::string&); - void print_sdl_error(const std::string&); /* Returns an unsorted vector of keys from the passed map */ template class Map>