diff --git a/CMakeLists.txt b/CMakeLists.txt index e673fb7..ca91615 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -76,7 +76,7 @@ else() target_compile_definitions(${EXECUTABLE_TARGET} PRIVATE NDEBUG) endif() -# Set C++20 standard +# Set C++ standard set_target_properties(${EXECUTABLE_TARGET} PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED ON diff --git a/src/application.cpp b/src/application.cpp index c0e4e98..f39672e 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -18,21 +18,19 @@ */ #include "application.hpp" -#include "debug/logger.hpp" -#include "event/event-dispatcher.hpp" -#include "event/window-events.hpp" +#include "config.hpp" +#include "debug/log.hpp" #include "input/scancode.hpp" -#include "input/sdl-game-controller-tables.hpp" -#include "input/sdl-scancode-table.hpp" +#include "math/map.hpp" #include "resources/image.hpp" #include #include +#include +#include #include #include -#include -#include -application::application(debug::logger& log): +application::application(): closed(false), fullscreen(true), v_sync(false), @@ -42,133 +40,148 @@ application::application(debug::logger& log): window_dimensions({0, 0}), viewport_dimensions({0, 0}), mouse_position({0, 0}), - logger(&log), sdl_window(nullptr), sdl_gl_context(nullptr) { - // Get SDL compiled version + // Log SDL compiled version SDL_version sdl_compiled_version; SDL_VERSION(&sdl_compiled_version); - std::string sdl_compiled_version_string = std::to_string(sdl_compiled_version.major) + "." + std::to_string(sdl_compiled_version.minor) + "." + std::to_string(sdl_compiled_version.patch); - - // Get SDL linked version + debug::log::debug("Compiled against SDL {}.{}.{}", sdl_compiled_version.major, sdl_compiled_version.minor, sdl_compiled_version.patch); + + // Log SDL linked version SDL_version sdl_linked_version; SDL_GetVersion(&sdl_linked_version); - std::string sdl_linked_version_string = std::to_string(sdl_linked_version.major) + "." + std::to_string(sdl_linked_version.minor) + "." + std::to_string(sdl_linked_version.patch); - - // Init SDL - logger->push_task("Initializing SDL"); - logger->log("Compiled against SDL " + sdl_compiled_version_string); - logger->log("Linking against SDL " + sdl_linked_version_string); - if (SDL_Init(SDL_INIT_VIDEO) != 0) - { - logger->pop_task(EXIT_FAILURE); - throw std::runtime_error("Failed to initialize SDL"); - } - else - { - logger->pop_task(EXIT_SUCCESS); - } + debug::log::debug("Linking against SDL {}.{}.{}", sdl_linked_version.major, sdl_linked_version.minor, sdl_linked_version.patch); - // Load default OpenGL library - logger->push_task("Loading OpenGL library"); - if (SDL_GL_LoadLibrary(nullptr) != 0) + // Init SDL events and video subsystems + debug::log::trace("Initializing SDL events and video subsystems..."); + if (SDL_InitSubSystem(SDL_INIT_EVENTS | SDL_INIT_VIDEO) != 0) { - logger->pop_task(EXIT_FAILURE); + debug::log::fatal("Failed to initialize SDL events and video subsystems: {}", SDL_GetError()); + throw std::runtime_error("Failed to initialize SDL events and video subsystems"); } - else - { - logger->pop_task(EXIT_SUCCESS); - } - - // Set window creation hints - SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + debug::log::trace("Initialized SDL events and video subsystems"); - // Get display dimensions + // Detect display dimensions SDL_DisplayMode sdl_desktop_display_mode; if (SDL_GetDesktopDisplayMode(0, &sdl_desktop_display_mode) != 0) { - logger->error("Failed to detect desktop display mode: \"" + std::string(SDL_GetError()) + "\""); + debug::log::fatal("Failed to detect desktop display mode: {}", SDL_GetError()); throw std::runtime_error("Failed to detect desktop display mode"); } - else - { - display_dimensions = {sdl_desktop_display_mode.w, sdl_desktop_display_mode.h}; - } + display_dimensions = {sdl_desktop_display_mode.w, sdl_desktop_display_mode.h}; - // Get display DPI + // Detect display DPI if (SDL_GetDisplayDPI(0, &display_dpi, nullptr, nullptr) != 0) { - logger->error("Failed to detect display DPI: \"" + std::string(SDL_GetError()) + "\""); + debug::log::fatal("Failed to detect display DPI: {}", SDL_GetError()); throw std::runtime_error("Failed to detect display DPI"); } - else + + // Log display properties + debug::log::info("Detected {}x{}@{}Hz display with {} DPI", sdl_desktop_display_mode.w, sdl_desktop_display_mode.h, sdl_desktop_display_mode.refresh_rate, display_dpi); + + // Load OpenGL library + debug::log::trace("Loading OpenGL library..."); + if (SDL_GL_LoadLibrary(nullptr) != 0) { - logger->log("Detected " + std::to_string(sdl_desktop_display_mode.w) + "x" + std::to_string(sdl_desktop_display_mode.h) + " display with " + std::to_string(display_dpi) + " DPI"); + debug::log::fatal("Failed to load OpenGL library: {}", SDL_GetError()); + throw std::runtime_error("Failed to load OpenGL library"); } + debug::log::trace("Loaded OpenGL library"); + + // Set OpenGL-related window creation hints + SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, config::opengl_version_major); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, config::opengl_version_minor); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, config::opengl_min_red_size); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, config::opengl_min_green_size); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, config::opengl_min_blue_size); + SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, config::opengl_min_alpha_size); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, config::opengl_min_depth_size); + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, config::opengl_min_stencil_size); // Create a hidden fullscreen window - logger->push_task("Creating " + std::to_string(display_dimensions[0]) + "x" + std::to_string(display_dimensions[1]) + " window"); + debug::log::trace("Creating {}x{} window...", display_dimensions[0], display_dimensions[1]); sdl_window = SDL_CreateWindow ( - "Antkeeper", + config::application_name, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, display_dimensions[0], display_dimensions[1], SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN ); - if (!sdl_window) { - logger->pop_task(EXIT_FAILURE); + debug::log::fatal("Failed to create {}x{} window: {}", display_dimensions[0], display_dimensions[1], SDL_GetError()); throw std::runtime_error("Failed to create SDL window"); } - else - { - logger->pop_task(EXIT_SUCCESS); - } - + debug::log::trace("Created {}x{} window", display_dimensions[0], display_dimensions[1]); + // Create OpenGL context - logger->push_task("Creating OpenGL 3.3 context"); + debug::log::trace("Creating OpenGL {}.{} context...", config::opengl_version_major, config::opengl_version_minor); sdl_gl_context = SDL_GL_CreateContext(sdl_window); if (!sdl_gl_context) { - logger->pop_task(EXIT_FAILURE); + debug::log::fatal("Failed to create OpenGL context: {}", SDL_GetError()); throw std::runtime_error("Failed to create OpenGL context"); } - else - { - logger->pop_task(EXIT_SUCCESS); - } - // Make OpenGL context current - logger->push_task("Making OpenGL context current"); - if (SDL_GL_MakeCurrent(sdl_window, sdl_gl_context) != 0) + // Log version of created OpenGL context + int opengl_context_version_major = -1; + int opengl_context_version_minor = -1; + SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &opengl_context_version_major); + SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &opengl_context_version_minor); + debug::log::info("Created OpenGL {}.{} context", opengl_context_version_major, opengl_context_version_minor); + + // Compare OpenGL context version with requested version + if (opengl_context_version_major != config::opengl_version_major || + opengl_context_version_minor != config::opengl_version_minor) { - logger->pop_task(EXIT_FAILURE); - throw std::runtime_error("Failed to make OpenGL context current"); + debug::log::warning("Requested OpenGL {}.{} context but created OpenGL {}.{} context", config::opengl_version_major, config::opengl_version_minor, opengl_context_version_major, opengl_context_version_minor); } - else + + // Log format of OpenGL context default framebuffer + int opengl_context_red_size = -1; + int opengl_context_green_size = -1; + int opengl_context_blue_size = -1; + int opengl_context_alpha_size = -1; + int opengl_context_depth_size = -1; + int opengl_context_stencil_size = -1; + SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &opengl_context_red_size); + SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &opengl_context_green_size); + SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &opengl_context_blue_size); + SDL_GL_GetAttribute(SDL_GL_ALPHA_SIZE, &opengl_context_alpha_size); + SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &opengl_context_depth_size); + SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &opengl_context_stencil_size); + debug::log::info("OpenGL context default framebuffer format: R{}G{}B{}A{}D{}S{}", opengl_context_red_size, opengl_context_green_size, opengl_context_blue_size, opengl_context_alpha_size, opengl_context_depth_size, opengl_context_stencil_size); + + // Compare OpenGL context default framebuffer format with request format + if (opengl_context_red_size < config::opengl_min_red_size || + opengl_context_green_size < config::opengl_min_green_size || + opengl_context_blue_size < config::opengl_min_blue_size || + opengl_context_alpha_size < config::opengl_min_alpha_size || + opengl_context_depth_size < config::opengl_min_depth_size || + opengl_context_stencil_size < config::opengl_min_stencil_size) { - logger->pop_task(EXIT_SUCCESS); + debug::log::warning + ( + "OpenGL default framebuffer format (R{}G{}B{}A{}D{}S{}) does not meet minimum requested format (R{}G{}B{}A{}D{}S{})", + opengl_context_red_size, opengl_context_green_size, opengl_context_blue_size, opengl_context_alpha_size, opengl_context_depth_size, opengl_context_stencil_size, + config::opengl_min_red_size, config::opengl_min_green_size, config::opengl_min_blue_size, config::opengl_min_alpha_size, config::opengl_min_depth_size, config::opengl_min_stencil_size + ); } - + // Load OpenGL functions via GLAD - logger->push_task("Loading OpenGL functions"); + debug::log::trace("Loading OpenGL functions..."); if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) { - logger->pop_task(EXIT_FAILURE); + debug::log::fatal("Failed to load OpenGL functions", SDL_GetError()); throw std::runtime_error("Failed to load OpenGL functions"); } - else - { - logger->pop_task(EXIT_SUCCESS); - } + debug::log::trace("Loaded OpenGL functions"); // Update window size and viewport size SDL_GetWindowSize(sdl_window, &window_dimensions[0], &window_dimensions[1]); @@ -177,28 +190,27 @@ application::application(debug::logger& log): // Set v-sync mode set_v_sync(true); - // Init SDL joystick and gamepad subsystems - logger->push_task("Initializing SDL Joystick and Game Controller subsystems"); + // Init SDL joystick and controller subsystems + debug::log::trace("Initializing SDL joystick and controller subsystems..."); if (SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) != 0) { - logger->pop_task(EXIT_FAILURE); + debug::log::error("Failed to initialize SDL joystick and controller subsytems: {}", SDL_GetError()); } else { - logger->pop_task(EXIT_SUCCESS); + debug::log::trace("Initialized SDL joystick and controller subsystems"); } // Setup rasterizer rasterizer = new gl::rasterizer(); - // Setup events - event_dispatcher = new ::event_dispatcher(); + // Register keyboard and mouse with input device manager + device_manager.register_device(keyboard); + device_manager.register_device(mouse); - // Setup input - keyboard = new input::keyboard(); - keyboard->set_event_dispatcher(event_dispatcher); - mouse = new input::mouse(); - mouse->set_event_dispatcher(event_dispatcher); + // Generate keyboard and mouse device connected events + keyboard.connect(); + mouse.connect(); // Connect gamepads process_events(); @@ -246,7 +258,9 @@ void application::set_relative_mouse_mode(bool enabled) SDL_SetRelativeMouseMode(SDL_FALSE); SDL_WarpMouseInWindow(sdl_window, mouse_position[0], mouse_position[1]); if (cursor_visible) + { SDL_ShowCursor(SDL_ENABLE); + } } } @@ -287,39 +301,38 @@ void application::set_v_sync(bool v_sync) { if (v_sync) { - logger->push_task("Enabling adaptive v-sync"); + debug::log::trace("Enabling adaptive v-sync..."); if (SDL_GL_SetSwapInterval(-1) != 0) { - logger->pop_task(EXIT_FAILURE); - - logger->push_task("Enabling synchronized v-sync"); + debug::log::error("Failed to enable adaptive v-sync: {}", SDL_GetError()); + debug::log::trace("Enabling synchronized v-sync..."); if (SDL_GL_SetSwapInterval(1) != 0) { - logger->pop_task(EXIT_FAILURE); + debug::log::error("Failed to enable synchronized v-sync: {}", SDL_GetError()); } else { this->v_sync = v_sync; - logger->pop_task(EXIT_SUCCESS); + debug::log::debug("Enabled synchronized v-sync"); } } else { this->v_sync = v_sync; - logger->pop_task(EXIT_SUCCESS); + debug::log::debug("Enabled adaptive v-sync"); } } else { - logger->push_task("Disabling v-sync"); + debug::log::trace("Disabling v-sync..."); if (SDL_GL_SetSwapInterval(0) != 0) { - logger->pop_task(EXIT_FAILURE); + debug::log::error("Failed to disable v-sync: {}", SDL_GetError()); } else { this->v_sync = v_sync; - logger->pop_task(EXIT_SUCCESS); + debug::log::debug("Disabled v-sync"); } } } @@ -348,227 +361,272 @@ void application::hide_window() void application::add_game_controller_mappings(const void* mappings, std::size_t size) { - logger->push_task("Adding SDL game controller mappings"); + debug::log::trace("Adding SDL game controller mappings..."); int mapping_count = SDL_GameControllerAddMappingsFromRW(SDL_RWFromConstMem(mappings, static_cast(size)), 0); if (mapping_count == -1) { - logger->pop_task(EXIT_FAILURE); + debug::log::error("Failed to add SDL game controller mappings: {}", SDL_GetError()); } else { - logger->log("Added " + std::to_string(mapping_count) + " SDL game controller mappings"); - logger->pop_task(EXIT_SUCCESS); + debug::log::debug("Added {} SDL game controller mappings", mapping_count); } } void application::process_events() { + // Active modifier keys + std::uint16_t sdl_key_mod = KMOD_NONE; + std::uint16_t modifier_keys = input::modifier_key::none; + // Mouse motion event accumulators - bool mouse_motion = false; - int mouse_x; - int mouse_y; - int mouse_dx = 0; - int mouse_dy = 0; + // bool mouse_motion = false; + // std::int32_t mouse_x; + // std::int32_t mouse_y; + // std::int32_t mouse_dx = 0; + // std::int32_t mouse_dy = 0; SDL_Event sdl_event; while (SDL_PollEvent(&sdl_event)) { switch (sdl_event.type) { + [[likely]] case SDL_MOUSEMOTION: + { + // More than one mouse motion event is often generated per frame, and may be a source of lag. + // Mouse events can be accumulated here to prevent excessive function calls and allocations + // mouse_motion = true; + // mouse_x = sdl_event.motion.x; + // mouse_y = sdl_event.motion.y; + // mouse_dx += sdl_event.motion.xrel; + // mouse_dy += sdl_event.motion.yrel; + + mouse.move({sdl_event.motion.x, sdl_event.motion.y}, {sdl_event.motion.xrel, sdl_event.motion.yrel}); + break; + } + case SDL_KEYDOWN: case SDL_KEYUP: { - if (sdl_event.key.repeat == 0) + // Get scancode of key + const input::scancode scancode = static_cast(sdl_event.key.keysym.scancode); + + // Rebuild modifier keys bit mask + if (sdl_event.key.keysym.mod != sdl_key_mod) { - input::scancode scancode = input::scancode::unknown; - if (sdl_event.key.keysym.scancode <= SDL_SCANCODE_APP2) - { - scancode = input::sdl_scancode_table[sdl_event.key.keysym.scancode]; - } - - if (sdl_event.type == SDL_KEYDOWN) - keyboard->press(scancode); - else - keyboard->release(scancode); + sdl_key_mod = sdl_event.key.keysym.mod; + + modifier_keys = input::modifier_key::none; + if (sdl_key_mod & KMOD_LSHIFT) + modifier_keys |= input::modifier_key::left_shift; + if (sdl_key_mod & KMOD_RSHIFT) + modifier_keys |= input::modifier_key::right_shift; + if (sdl_key_mod & KMOD_LCTRL) + modifier_keys |= input::modifier_key::left_ctrl; + if (sdl_key_mod & KMOD_RCTRL) + modifier_keys |= input::modifier_key::right_ctrl; + if (sdl_key_mod & KMOD_LGUI) + modifier_keys |= input::modifier_key::left_gui; + if (sdl_key_mod & KMOD_RGUI) + modifier_keys |= input::modifier_key::right_gui; + if (sdl_key_mod & KMOD_NUM) + modifier_keys |= input::modifier_key::num_lock; + if (sdl_key_mod & KMOD_CAPS) + modifier_keys |= input::modifier_key::caps_lock; + if (sdl_key_mod & KMOD_SCROLL) + modifier_keys |= input::modifier_key::scroll_lock; + if (sdl_key_mod & KMOD_MODE) + modifier_keys |= input::modifier_key::alt_gr; + } + + // Determine if event was generated from a key repeat + const bool repeat = sdl_event.key.repeat > 0; + + if (sdl_event.type == SDL_KEYDOWN) + { + keyboard.press(scancode, repeat, modifier_keys); } + else + { + keyboard.release(scancode, repeat, modifier_keys); + } + break; } - - case SDL_MOUSEMOTION: + + case SDL_MOUSEWHEEL: { - // More than one mouse motion event is often generated per frame, and may be a source of lag. - // Mouse events are accumulated here to prevent excess function calls and allocations - mouse_motion = true; - mouse_x = sdl_event.motion.x; - mouse_y = sdl_event.motion.y; - mouse_dx += sdl_event.motion.xrel; - mouse_dy += sdl_event.motion.yrel; + const float flip = (sdl_event.wheel.direction == SDL_MOUSEWHEEL_FLIPPED) ? -1.0f : 1.0f; + mouse.scroll({sdl_event.wheel.preciseX * flip, sdl_event.wheel.preciseY * flip}); break; } - + case SDL_MOUSEBUTTONDOWN: { - mouse->press(sdl_event.button.button, sdl_event.button.x, sdl_event.button.y); + mouse.press(static_cast(sdl_event.button.button)); break; } case SDL_MOUSEBUTTONUP: { - mouse->release(sdl_event.button.button, sdl_event.button.x, sdl_event.button.y); + mouse.release(static_cast(sdl_event.button.button)); break; } - case SDL_MOUSEWHEEL: + [[likely]] case SDL_CONTROLLERAXISMOTION: { - int direction = (sdl_event.wheel.direction == SDL_MOUSEWHEEL_FLIPPED) ? -1 : 1; - mouse->scroll(sdl_event.wheel.x * direction, sdl_event.wheel.y * direction); + if (sdl_event.caxis.axis != SDL_CONTROLLER_AXIS_INVALID) + { + if (auto it = gamepad_map.find(sdl_event.cdevice.which); it != gamepad_map.end()) + { + // Map axis position onto `[-1, 1]`. + const float position = math::map + ( + static_cast(sdl_event.caxis.value), + static_cast(std::numeric_limits::min()), + static_cast(std::numeric_limits::max()), + -1.0f, + 1.0f + ); + + // Generate gamepad axis moved event + it->second->move(static_cast(sdl_event.caxis.axis), position); + } + } break; } - + case SDL_CONTROLLERBUTTONDOWN: { if (sdl_event.cbutton.button != SDL_CONTROLLER_BUTTON_INVALID) { if (auto it = gamepad_map.find(sdl_event.cdevice.which); it != gamepad_map.end()) { - input::gamepad_button button = input::sdl_button_table[sdl_event.cbutton.button]; - it->second->press(button); + it->second->press(static_cast(sdl_event.cbutton.button)); } } break; } - + case SDL_CONTROLLERBUTTONUP: { if (sdl_event.cbutton.button != SDL_CONTROLLER_BUTTON_INVALID) { if (auto it = gamepad_map.find(sdl_event.cdevice.which); it != gamepad_map.end()) { - input::gamepad_button button = input::sdl_button_table[sdl_event.cbutton.button]; - it->second->release(button); + it->second->release(static_cast(sdl_event.cbutton.button)); } } break; } - - case SDL_CONTROLLERAXISMOTION: + + case SDL_WINDOWEVENT: { - if (sdl_event.caxis.axis != SDL_CONTROLLER_AXIS_INVALID) + switch (sdl_event.window.event) { - if (auto it = gamepad_map.find(sdl_event.cdevice.which); it != gamepad_map.end()) - { - input::gamepad_axis axis = input::sdl_axis_table[sdl_event.caxis.axis]; - float value = sdl_event.caxis.value; - static const float min = static_cast(std::numeric_limits::min()); - static const float max = static_cast(std::numeric_limits::max()); - value /= (value < 0.0f) ? -min : max; - it->second->move(axis, value); - } + case SDL_WINDOWEVENT_SIZE_CHANGED: + window_resized(); + break; + + case SDL_WINDOWEVENT_FOCUS_GAINED: + debug::log::debug("Window focus gained"); + window_focus_changed_publisher.publish({true}); + break; + + case SDL_WINDOWEVENT_FOCUS_LOST: + debug::log::debug("Window focus lost"); + window_focus_changed_publisher.publish({false}); + break; + + case SDL_WINDOWEVENT_MOVED: + debug::log::trace("Window moved to ({}, {})", sdl_event.window.data1, sdl_event.window.data2); + window_moved_publisher.publish({static_cast(sdl_event.window.data1), static_cast(sdl_event.window.data2)}); + break; + + [[unlikely]] case SDL_WINDOWEVENT_CLOSE: + debug::log::info("Window closed"); + window_closed_publisher.publish({}); + break; + + default: + break; } break; } - - case SDL_CONTROLLERDEVICEADDED: + + [[unlikely]] case SDL_CONTROLLERDEVICEADDED: { if (SDL_IsGameController(sdl_event.cdevice.which)) { SDL_GameController* sdl_controller = SDL_GameControllerOpen(sdl_event.cdevice.which); - - std::string controller_name = SDL_GameControllerNameForIndex(sdl_event.cdevice.which); + const char* controller_name = SDL_GameControllerNameForIndex(sdl_event.cdevice.which); + if (sdl_controller) { if (auto it = gamepad_map.find(sdl_event.cdevice.which); it != gamepad_map.end()) { - logger->log("Reconnected gamepad \"" + controller_name + "\""); - - it->second->connect(true); - gamepad_connection_signal.emit(*it->second, true); + // Gamepad reconnected + debug::log::info("Reconnected gamepad \"{}\"", controller_name); + it->second->connect(); } else { // Get gamepad GUID SDL_Joystick* sdl_joystick = SDL_GameControllerGetJoystick(sdl_controller); SDL_JoystickGUID sdl_guid = SDL_JoystickGetGUID(sdl_joystick); - char guid_buffer[33]; - std::memset(guid_buffer, '\0', 33); - SDL_JoystickGetGUIDString(sdl_guid, &guid_buffer[0], 33); - std::string guid(guid_buffer); - logger->log("Connected gamepad \"" + controller_name + "\" with GUID: " + guid + ""); + // Copy into UUID struct + ::uuid gamepad_uuid; + std::memcpy(gamepad_uuid.data.data(), sdl_guid.data, gamepad_uuid.data.size()); + + debug::log::info("Connected gamepad \"{}\" with UUID {}", controller_name, gamepad_uuid.string()); // Create new gamepad input::gamepad* gamepad = new input::gamepad(); - gamepad->set_event_dispatcher(event_dispatcher); - gamepad->set_guid(guid); + gamepad->set_uuid(gamepad_uuid); - // Add gamepad to list and map - gamepads.push_back(gamepad); + // Add gamepad to gamepad map gamepad_map[sdl_event.cdevice.which] = gamepad; - // Send controller connected event - gamepad->connect(false); - gamepad_connection_signal.emit(*it->second, false); + // Register gamepad with device manager + device_manager.register_device(*gamepad); + + // Generate gamepad connected event + gamepad->connect(); } } else { - logger->error("Failed to connected gamepad \"" + controller_name + "\""); + debug::log::error("Failed to connected gamepad \"{}\": {}", controller_name, SDL_GetError()); } } break; } - - case SDL_CONTROLLERDEVICEREMOVED: + + [[unlikely]] case SDL_CONTROLLERDEVICEREMOVED: { SDL_GameController* sdl_controller = SDL_GameControllerFromInstanceID(sdl_event.cdevice.which); - + if (sdl_controller) { - SDL_GameControllerClose(sdl_controller); - logger->log("Disconnected gamepad"); + const char* controller_name = SDL_GameControllerNameForIndex(sdl_event.cdevice.which); + SDL_GameControllerClose(sdl_controller); if (auto it = gamepad_map.find(sdl_event.cdevice.which); it != gamepad_map.end()) { it->second->disconnect(); } - } - - break; - } - - case SDL_WINDOWEVENT: - { - switch (sdl_event.window.event) - { - case SDL_WINDOWEVENT_SIZE_CHANGED: - window_resized(); - break; - case SDL_WINDOWEVENT_FOCUS_GAINED: - window_focus_signal.emit(true); - break; - - case SDL_WINDOWEVENT_FOCUS_LOST: - window_focus_signal.emit(false); - break; - - case SDL_WINDOWEVENT_MOVED: - window_motion_signal.emit(sdl_event.window.data1, sdl_event.window.data2); - break; - - case SDL_WINDOWEVENT_CLOSE: - window_close_signal.emit(); - break; - - default: - break; + debug::log::info("Disconnected gamepad \"{}\"", controller_name); } + break; } - - case SDL_QUIT: + + [[unlikely]] case SDL_QUIT: { + debug::log::info("Quit requested"); close(); break; } @@ -576,10 +634,10 @@ void application::process_events() } // Process accumulated mouse motion events - if (mouse_motion) - { - mouse->move(mouse_x, mouse_y, mouse_dx, mouse_dy); - } + // if (mouse_motion) + // { + // mouse.move(mouse_x, mouse_y, mouse_dx, mouse_dy); + // } } void application::window_resized() @@ -588,14 +646,17 @@ void application::window_resized() SDL_GetWindowSize(sdl_window, &window_dimensions[0], &window_dimensions[1]); SDL_GL_GetDrawableSize(sdl_window, &viewport_dimensions[0], &viewport_dimensions[1]); - rasterizer->context_resized(viewport_dimensions[0], viewport_dimensions[1]); - - window_resized_event event; - event.w = window_dimensions[0]; - event.h = window_dimensions[1]; + debug::log::debug("Window resized to {}x{}", window_dimensions[0], window_dimensions[1]); - event_dispatcher->queue(event); + rasterizer->context_resized(viewport_dimensions[0], viewport_dimensions[1]); - window_size_signal.emit(window_dimensions[0], window_dimensions[1]); - viewport_size_signal.emit(viewport_dimensions[0], viewport_dimensions[1]); + window_resized_publisher.publish + ( + { + window_dimensions[0], + window_dimensions[1], + viewport_dimensions[0], + viewport_dimensions[1] + } + ); } diff --git a/src/application.hpp b/src/application.hpp index b3ea474..fb57b3f 100644 --- a/src/application.hpp +++ b/src/application.hpp @@ -20,21 +20,21 @@ #ifndef ANTKEEPER_APPLICATION_HPP #define ANTKEEPER_APPLICATION_HPP -#include -#include -#include +#include "event/publisher.hpp" #include "gl/rasterizer.hpp" +#include "input/device-manager.hpp" +#include "input/event.hpp" +#include "input/gamepad.hpp" #include "input/keyboard.hpp" #include "input/mouse.hpp" -#include "input/gamepad.hpp" #include "utility/fundamental-types.hpp" -#include "debug/logger.hpp" -#include "event/signal.hpp" +#include +#include +#include // Forward declarations typedef struct SDL_Window SDL_Window; typedef void* SDL_GLContext; -class event_dispatcher; /** * @@ -43,14 +43,12 @@ class application { public: /** - * Creates and initializes an application. - * - * @param logger Debug logger. + * Constructs and initializes an application. */ - application(debug::logger& log); + application(); /** - * Destroys an application. + * Destructs an application. */ ~application(); @@ -112,62 +110,67 @@ public: void add_game_controller_mappings(const void* mappings, std::size_t size); /// Returns the dimensions of the current display. - const int2& get_display_dimensions() const; + [[nodiscard]] const int2& get_display_dimensions() const; /// Returns the DPI of the display. - float get_display_dpi() const; + [[nodiscard]] float get_display_dpi() const; /// Returns the dimensions of the window. - const int2& get_window_dimensions() const; + [[nodiscard]] const int2& get_window_dimensions() const; /// Returns the dimensions of the window's drawable viewport. - const int2& get_viewport_dimensions() const; + [[nodiscard]] const int2& get_viewport_dimensions() const; /// Returns `true` if the window is in fullscreen mode, `false` otherwise. - bool is_fullscreen() const; + [[nodiscard]] bool is_fullscreen() const; /// Returns `true` if the v-sync is enabled, `false` otherwise. - bool get_v_sync() const; + [[nodiscard]] bool get_v_sync() const; /// Returns the rasterizer for the window. - gl::rasterizer* get_rasterizer(); - - /// Returns the application logger. - debug::logger* get_logger(); - - /// Returns the virtual keyboard. - input::keyboard* get_keyboard(); - - /// Returns the virtual mouse. - input::mouse* get_mouse(); - - /// Returns the list of virtual gamepads. - const std::list& get_gamepads(); - - /// Returns the application's event dispatcher. - event_dispatcher* get_event_dispatcher(); + [[nodiscard]] gl::rasterizer* get_rasterizer(); void process_events(); - bool was_closed() const; - - /// Returns a connector for the signal emitted when a gamepad is connected or disconnected. - connector& get_gamepad_connection_signal() noexcept; + [[nodiscard]] bool was_closed() const; - /// Returns a connector for the signal emitted when the window is requested to close. - connector& get_window_close_signal() noexcept; - - /// Returns a connector for the signal emitted each time the window gains or loses focus. - connector& get_window_focus_signal() noexcept; - - /// Returns a connector for the signal emitted each time the window is moved. - connector& get_window_motion_signal() noexcept; - - /// Returns a connector for the signal emitted each time the window is resized. - connector& get_window_size_signal() noexcept; - - /// Returns a connector for the signal emitted each time the window viewport is resized. - connector& get_viewport_size_signal() noexcept; + /** + * Returns the input device manager. + */ + /// @{ + [[nodiscard]] inline const input::device_manager& get_device_manager() const noexcept + { + return device_manager; + } + [[nodiscard]] inline input::device_manager& get_device_manager() noexcept + { + return device_manager; + } + /// @} + + /// Returns the channel through which window closed events are published. + [[nodiscard]] inline event::channel& get_window_closed_channel() noexcept + { + return window_closed_publisher.channel(); + } + + /// Returns the channel through which window focus changed events are published. + [[nodiscard]] inline event::channel& get_window_focus_changed_channel() noexcept + { + return window_focus_changed_publisher.channel(); + } + + /// Returns the channel through which window moved events are published. + [[nodiscard]] inline event::channel& get_window_moved_channel() noexcept + { + return window_moved_publisher.channel(); + } + + /// Returns the channel through which window resized events are published. + [[nodiscard]] inline event::channel& get_window_resized_channel() noexcept + { + return window_resized_publisher.channel(); + } private: void window_resized(); @@ -181,35 +184,24 @@ private: int2 window_dimensions; int2 viewport_dimensions; int2 mouse_position; - debug::logger* logger; - + SDL_Window* sdl_window; SDL_GLContext sdl_gl_context; gl::rasterizer* rasterizer; - // Events - event_dispatcher* event_dispatcher; - // Input devices - input::keyboard* keyboard; - input::mouse* mouse; - std::list gamepads; + input::device_manager device_manager; + input::keyboard keyboard; + input::mouse mouse; std::unordered_map gamepad_map; - signal gamepad_connection_signal; - signal window_close_signal; - signal window_focus_signal; - signal window_motion_signal; - signal window_size_signal; - signal viewport_size_signal; + event::publisher window_closed_publisher; + event::publisher window_focus_changed_publisher; + event::publisher window_moved_publisher; + event::publisher window_resized_publisher; }; -inline debug::logger* application::get_logger() -{ - return logger; -} - inline const int2& application::get_display_dimensions() const { return display_dimensions; @@ -245,59 +237,9 @@ inline gl::rasterizer* application::get_rasterizer() return rasterizer; } -inline input::keyboard* application::get_keyboard() -{ - return keyboard; -} - -inline input::mouse* application::get_mouse() -{ - return mouse; -} - -inline const std::list& application::get_gamepads() -{ - return gamepads; -} - -inline event_dispatcher* application::get_event_dispatcher() -{ - return event_dispatcher; -} - inline bool application::was_closed() const { return closed; } -inline connector& application::get_gamepad_connection_signal() noexcept -{ - return gamepad_connection_signal.connector(); -} - -inline connector& application::get_window_close_signal() noexcept -{ - return window_close_signal.connector(); -} - -inline connector& application::get_window_focus_signal() noexcept -{ - return window_focus_signal.connector(); -} - -inline connector& application::get_window_motion_signal() noexcept -{ - return window_motion_signal.connector(); -} - -inline connector& application::get_window_size_signal() noexcept -{ - return window_size_signal.connector(); -} - -inline connector& application::get_viewport_size_signal() noexcept -{ - return viewport_size_signal.connector(); -} - #endif // ANTKEEPER_APPLICATION_HPP diff --git a/src/color/cct.hpp b/src/color/cct.hpp index 16f3b34..9603858 100644 --- a/src/color/cct.hpp +++ b/src/color/cct.hpp @@ -20,8 +20,8 @@ #ifndef ANTKEEPER_COLOR_CCT_HPP #define ANTKEEPER_COLOR_CCT_HPP -#include "ucs.hpp" -#include "xyy.hpp" +#include "color/ucs.hpp" +#include "color/xyy.hpp" #include "math/vector.hpp" namespace color { diff --git a/src/color/color.hpp b/src/color/color.hpp index ee55470..2910ae1 100644 --- a/src/color/color.hpp +++ b/src/color/color.hpp @@ -23,15 +23,15 @@ /// Color manipulation. namespace color {} -#include "aces.hpp" -#include "cat.hpp" -#include "cct.hpp" -#include "illuminant.hpp" -#include "index.hpp" -#include "rgb.hpp" -#include "srgb.hpp" -#include "ucs.hpp" -#include "xyy.hpp" -#include "xyz.hpp" +#include "color/aces.hpp" +#include "color/cat.hpp" +#include "color/cct.hpp" +#include "color/illuminant.hpp" +#include "color/index.hpp" +#include "color/rgb.hpp" +#include "color/srgb.hpp" +#include "color/ucs.hpp" +#include "color/xyy.hpp" +#include "color/xyz.hpp" #endif // ANTKEEPER_COLOR_HPP diff --git a/src/config.hpp.in b/src/config.hpp.in index 3407224..5ae32fe 100644 --- a/src/config.hpp.in +++ b/src/config.hpp.in @@ -20,15 +20,74 @@ #ifndef ANTKEEPER_CONFIG_HPP #define ANTKEEPER_CONFIG_HPP +// Disable trace message logging on release builds +#if defined(NDEBUG) + #define ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY 1 +#else + #define ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY 0 +#endif + #include "math/vector.hpp" /// Global configuration constants. namespace config { -constexpr int version_major = @PROJECT_VERSION_MAJOR@; -constexpr int version_minor = @PROJECT_VERSION_MINOR@; -constexpr int version_patch = @PROJECT_VERSION_PATCH@; -constexpr const char* version_string = "@PROJECT_VERSION@"; +/// @name Application config +/// @{ + +/// Application name string. +constexpr const char* application_name = "@PROJECT_NAME@"; + +/// Application major version number. +constexpr int application_version_major = @PROJECT_VERSION_MAJOR@; + +/// Application minor version number. +constexpr int application_version_minor = @PROJECT_VERSION_MINOR@; + +/// Application patch version number. +constexpr int application_version_patch = @PROJECT_VERSION_PATCH@; + +/// Application version string ("`major.minor.patch`") +constexpr const char* application_version_string = "@PROJECT_VERSION@"; + +/// @} + +/// @name Debug config +/// @{ + +/// Maximum number of debug logs to archive. +constexpr std::size_t debug_log_archive_capacity = 5; + +/// @} + +/// @name OpenGL config +/// @{ + +/// OpenGL major version number, used when creating OpenGL contexts. +constexpr int opengl_version_major = 3; + +/// OpenGL minor version number, used when creating OpenGL contexts. +constexpr int opengl_version_minor = 3; + +/// Minimum number of bits in the red channel of the color attachment of the OpenGL default framebuffer. +constexpr int opengl_min_red_size = 8; + +/// Minimum number of bits in the green channel of the color attachment of the OpenGL default framebuffer. +constexpr int opengl_min_green_size = 8; + +/// Minimum number of bits in the blue channel of the color attachment of the OpenGL default framebuffer. +constexpr int opengl_min_blue_size = 8; + +/// Minimum number of bits in the alpha channel of the color attachment of the OpenGL default framebuffer. +constexpr int opengl_min_alpha_size = 0; + +/// Minimum number of bits in the depth attachment, if any, of the OpenGL default framebuffer. +constexpr int opengl_min_depth_size = 0; + +/// Minimum number of bits in the stencil attachment, if any, of the OpenGL default framebuffer. +constexpr int opengl_min_stencil_size = 0; + +/// @} constexpr math::vector global_forward = {0.0f, 0.0f, -1.0f}; constexpr math::vector global_up = {0.0f, 1.0f, 0.0f}; @@ -47,10 +106,10 @@ constexpr float menu_mouseover_padding = 0.1f; constexpr float menu_bg_opacity = 2.0f / 4.0f; /// RGBA color of active menu items. -constexpr float4 menu_active_color{1.0f, 1.0f, 1.0f, 1.0f}; +constexpr math::vector menu_active_color{1.0f, 1.0f, 1.0f, 1.0f}; /// RGBA color of inactive menu items. -constexpr float4 menu_inactive_color{1.0f, 1.0f, 1.0f, 0.5f}; +constexpr math::vector menu_inactive_color{1.0f, 1.0f, 1.0f, 0.5f}; /// Duration of the title screen fade in, in seconds. constexpr float title_fade_in_duration = 1.0f; diff --git a/src/debug/ansi-codes.hpp b/src/debug/ansi-codes.hpp deleted file mode 100644 index e5356b1..0000000 --- a/src/debug/ansi-codes.hpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_DEBUG_ANSI_CODES_HPP -#define ANTKEEPER_DEBUG_ANSI_CODES_HPP - -namespace debug { - -/// ANSI escape codes. -namespace ansi { - -constexpr char* reset = "\u004b[0m"; - -constexpr char* black = "\u003b[30m"; -constexpr char* red = "\u003b[31m"; -constexpr char* green = "\u003b[32m"; -constexpr char* yellow = "\u003b[33m"; -constexpr char* blue = "\u003b[34m"; -constexpr char* magenta = "\u003b[35m"; -constexpr char* cyan = "\u003b[36m"; -constexpr char* white = "\u003b[37m"; - -constexpr char* bright_black = "\u003b[30;1m"; -constexpr char* bright_red = "\u003b[31;1m"; -constexpr char* bright_green = "\u003b[32;1m"; -constexpr char* bright_yellow = "\u003b[33;1m"; -constexpr char* bright_blue = "\u003b[34;1m"; -constexpr char* bright_magenta = "\u003b[35;1m"; -constexpr char* bright_cyan = "\u003b[36;1m"; -constexpr char* bright_white = "\u003b[37;1m"; - -constexpr char* background_black = "\u003b[40m"; -constexpr char* background_red = "\u003b[41m"; -constexpr char* background_green = "\u003b[42m"; -constexpr char* background_yellow = "\u003b[43m"; -constexpr char* background_blue = "\u003b[44m"; -constexpr char* background_magenta = "\u003b[45m"; -constexpr char* background_cyan = "\u003b[46m"; -constexpr char* background_white = "\u003b[47m"; - -constexpr char* background_bright_black = "\u003b[40;1m"; -constexpr char* background_bright_red = "\u003b[41;1m"; -constexpr char* background_bright_green = "\u003b[42;1m"; -constexpr char* background_bright_yellow = "\u003b[43;1m"; -constexpr char* background_bright_blue = "\u003b[44;1m"; -constexpr char* background_bright_magenta = "\u003b[45;1m"; -constexpr char* background_bright_cyan = "\u003b[46;1m"; -constexpr char* background_bright_white = "\u003b[47;1m"; - -constexpr char* bold = "\u003b[1m"; -constexpr char* underline = "\u003b[4m"; -constexpr char* reversed = "\u001b[7m"; - -} // namespace ansi -} // namespace debug - -#endif // ANTKEEPER_DEBUG_ANSI_CODES_HPP - diff --git a/src/debug/cli.cpp b/src/debug/cli.cpp index c44f82d..e7a9ee4 100644 --- a/src/debug/cli.cpp +++ b/src/debug/cli.cpp @@ -17,7 +17,7 @@ * along with Antkeeper source code. If not, see . */ -#include "cli.hpp" +#include "debug/cli.hpp" namespace debug { @@ -26,12 +26,12 @@ std::string cli::interpret(const std::string& line) const std::istringstream stream(line); std::string command_name; stream >> command_name; - + if (auto it = commands.find(command_name); it != commands.end()) { return it->second(line.substr(command_name.length() + 1)); } - + return std::string(); } diff --git a/src/debug/cli.hpp b/src/debug/cli.hpp index 5f517ae..b1b194b 100644 --- a/src/debug/cli.hpp +++ b/src/debug/cli.hpp @@ -21,10 +21,11 @@ #define ANTKEEPER_DEBUG_CLI_HPP #include -#include #include #include #include +#include +#include namespace debug { @@ -41,12 +42,13 @@ public: * @return Stringified return value of the command function. */ std::string interpret(const std::string& line) const; - + /** * Registers a command with the CLI. * * @tparam T Command function return type. * @tparam Args Command function argument types. + * * @param name Command name. * @param function Command function. */ @@ -56,14 +58,14 @@ public: template void register_command(const std::string& name, T (*function)(Args...)); /// @} - + /** * Unregisters a command from the CLI. * * @param name Command name. */ void unregister_command(const std::string& name); - + private: /// String-wrapped function object typedef std::function command_type; @@ -71,21 +73,27 @@ private: /** * Parses a single argument from a string stream. * + * @tparam T Argument type. + * * @param stream String stream from which an argument should be parsed. */ template - static T parse(std::istringstream& stream); - + [[nodiscard]] static T parse(std::istringstream& stream); + /** - * Wraps a function in an interpreter function that parses strings as arguments then executes the wrapped function with the parsed arguments. + * Wraps a function in an interpreter function that parses strings as arguments, then executes the wrapped function with the parsed arguments. + * + * @tparam T Function return type. + * @tparam Args Function argument types. * * @param function Function to wrap. + * * @return Wrapped function. */ template - static command_type wrap(const std::function& function); - - std::map commands; + [[nodiscard]] static command_type wrap(std::function function); + + std::unordered_map commands; }; template @@ -109,26 +117,39 @@ T cli::parse(std::istringstream& stream) } template -typename cli::command_type cli::wrap(const std::function& function) +typename cli::command_type cli::wrap(std::function function) { - return std::bind( + return std::bind + ( [function](const std::string& line) -> std::string { - //std::size_t argument_count = sizeof...(Args); - + //constexpr std::size_t argument_count = sizeof...(Args); + // Parse string into tuple of arguments std::istringstream istream(line); std::tuple arguments{parse(istream)...}; - - // Invoke function with arguments and save the result - T result = std::apply(function, arguments); - - // Return function result as string - std::ostringstream ostream; - ostream << result; - return ostream.str(); + + if constexpr(std::is_void_v) + { + // Invoke function with parsed arguments + std::apply(function, arguments); + + // Return empty string + return std::string(); + } + else + { + // Invoke function with parsed arguments and save the result + T result = std::apply(function, arguments); + + // Return invocation result as a string + std::ostringstream ostream; + ostream << result; + return ostream.str(); + } }, - std::placeholders::_1); + std::placeholders::_1 + ); } } // namespace debug diff --git a/src/debug/console-commands.cpp b/src/debug/console-commands.cpp deleted file mode 100644 index bf437ab..0000000 --- a/src/debug/console-commands.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "console-commands.hpp" -#include "application.hpp" -#include "animation/timeline.hpp" -#include "debug/cli.hpp" - -namespace debug { -namespace cc { - -std::string echo(std::string text) -{ - return text; -} - -std::string exit(game::context* ctx) -{ - ctx->app->close(); - return std::string(); -} - -std::string scrot(game::context* ctx) -{ - //ctx->app->take_screenshot(); - return std::string("screenshot saved"); -} - -std::string cue(game::context* ctx, float t, std::string command) -{ - ::timeline* timeline = ctx->timeline; - debug::cli* cli = ctx->cli; - - timeline->add_cue({timeline->get_position() + t, std::function(std::bind(&debug::cli::interpret, cli, command))}); - - return std::string("command \"" + command + "\" will execute in " + std::to_string(t) + " seconds"); -} - -} // namespace cc -} // namespace debug diff --git a/src/debug/console.cpp b/src/debug/console.cpp new file mode 100644 index 0000000..b5109b1 --- /dev/null +++ b/src/debug/console.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "debug/console.hpp" + +#if defined(_WIN32) + #define WIN32_LEAN_AND_MEAN + #include +#endif + +namespace debug { +namespace console { + +void enable_vt100() +{ + #if defined(_WIN32) + DWORD mode = 0; + HANDLE std_output_handle = GetStdHandle(STD_OUTPUT_HANDLE); + GetConsoleMode(std_output_handle, &mode); + SetConsoleMode(std_output_handle, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING); + #endif +} + +void disable_vt100() +{ + #if defined(_WIN32) + DWORD mode = 0; + HANDLE std_output_handle = GetStdHandle(STD_OUTPUT_HANDLE); + GetConsoleMode(std_output_handle, &mode); + SetConsoleMode(std_output_handle, mode & (~ENABLE_VIRTUAL_TERMINAL_PROCESSING)); + #endif +} + +} // namespace console +} // namespace debug diff --git a/src/debug/console.hpp b/src/debug/console.hpp new file mode 100644 index 0000000..ef42254 --- /dev/null +++ b/src/debug/console.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_DEBUG_CONSOLE_HPP +#define ANTKEEPER_DEBUG_CONSOLE_HPP + +namespace debug { + +/** + * Debug console. + */ +namespace console { + +/// Enables VT100 virtual terminal sequences. +void enable_vt100(); + +/// Disables VT100 virtual terminal sequences. +void disable_vt100(); + +} // namespace console +} // namespace debug + +#endif // ANTKEEPER_DEBUG_CONSOLE_HPP diff --git a/src/debug/debug.hpp b/src/debug/debug.hpp index 5b38f45..e9548bc 100644 --- a/src/debug/debug.hpp +++ b/src/debug/debug.hpp @@ -20,12 +20,7 @@ #ifndef ANTKEEPER_DEBUG_HPP #define ANTKEEPER_DEBUG_HPP -/// Debugging functions and classes +/// Debugging functions and classes. namespace debug {} -#include "ansi-codes.hpp" -#include "cli.hpp" -#include "logger.hpp" -#include "performance-sampler.hpp" - #endif // ANTKEEPER_DEBUG_HPP diff --git a/src/debug/log.cpp b/src/debug/log.cpp new file mode 100644 index 0000000..ce4ad3f --- /dev/null +++ b/src/debug/log.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "debug/log.hpp" + +namespace debug { +namespace log { + +logger& default_logger() noexcept +{ + static logger instance; + return instance; +} + +void push_task(const std::string& description, std::source_location&& location) +{ + default_logger().log(description + " {", message_severity::info, std::forward(location)); +} + +void pop_task(int status, const std::string& description, std::source_location&& location) +{ + std::string message = "} => "; + if (status == EXIT_SUCCESS) + { + message += "success"; + + default_logger().log(std::move(message), message_severity::info, std::forward(location)); + } + else + { + message += "failure"; + if (!description.empty()) + { + message += "(" + description + ")"; + } + + default_logger().log(std::move(message), message_severity::error, std::forward(location)); + } +} + +} // namespace log +} // namespace debug diff --git a/src/debug/log.hpp b/src/debug/log.hpp new file mode 100644 index 0000000..2a3d9bb --- /dev/null +++ b/src/debug/log.hpp @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_DEBUG_LOG_HPP +#define ANTKEEPER_DEBUG_LOG_HPP + +#include "config.hpp" +#include "debug/log/message-severity.hpp" +#include "debug/log/logger.hpp" +#include +#include +#include + +// Enable logging of messages of all severities by default. +#if !defined(ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY) + #define ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY (ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_TRACE) +#endif + +namespace debug { + +/** + * Debug message logging. + */ +namespace log { + +/** + * Returns the default logger. + */ +[[nodiscard]] logger& default_logger() noexcept; + +/** + * Self-formatting message that logs itself to the default logger on construction. + * + * @tparam Severity Message severity. A message will not log itself if @p Severity is less than the user-defined macro `ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY`. + * @tparam Args Types of arguments to be formatted. + */ +template +struct message +{ + /** + * Formats and logs a message. + * + * Class template argument deduction (CTAD) is utilized to capture source location as a default argument following variadic format arguments. + * + * @param format Message format string. + * @param args Arguments to be formatted. + * @param location Source location from which the message was sent. + */ + message + ( + [[maybe_unused]] std::string_view format, + [[maybe_unused]] Args&&... args, + [[maybe_unused]] std::source_location&& location = std::source_location::current() + ) + { + if constexpr (ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY <= static_cast>(Severity)) + { + default_logger().log(std::vformat(format, std::make_format_args(std::forward(args)...)), Severity, std::forward(location)); + } + } +}; + +// Use class template argument deduction (CTAD) to capture source location as a default argument following variadic format arguments. +template +message(std::string_view, Args&&...) -> message; + +#if (ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY <= ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_TRACE) + /** + * Formats and logs a trace message. + * + * @param Args Types of arguments to be formatted. + */ + template + using trace = message; +#else + // Disable trace message logging. + inline void trace([[maybe_unused]] ...) noexcept {}; +#endif + +#if (ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY <= ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_DEBUG) + /** + * Formats and logs a debug message. + * + * @param Args Types of arguments to be formatted. + */ + template + using debug = message; +#else + // Disable debug message logging. + inline void debug([[maybe_unused]] ...) noexcept {}; +#endif + +#if (ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY <= ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_INFO) + /** + * Formats and logs an info message. + * + * @param Args Types of arguments to be formatted. + */ + template + using info = message; +#else + // Disable info message logging. + inline void info([[maybe_unused]] ...) noexcept {}; +#endif + +#if (ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY <= ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_WARNING) + /** + * Formats and logs a warning message. + * + * @param Args Types of arguments to be formatted. + */ + template + using warning = message; +#else + // Disable warning message logging. + inline void warning([[maybe_unused]] ...) noexcept {}; +#endif + +#if (ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY <= ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_ERROR) + /** + * Formats and logs an error message. + * + * @param Args Types of arguments to be formatted. + */ + template + using error = message; +#else + // Disable error message logging. + inline void error([[maybe_unused]] ...) noexcept {}; +#endif + +#if (ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY <= ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_FATAL) + /** + * Formats and logs a fatal error message. + * + * @param Args Types of arguments to be formatted. + */ + template + using fatal = message; +#else + // Disable fatal error message logging. + inline void fatal([[maybe_unused]] ...) noexcept {}; +#endif + +/** + * Pushes a task onto the default logger's task stack and writes its description the log. + * + * @param description Task description. + * @param location Source location from which the message was sent. + */ +void push_task +( + const std::string& description, + std::source_location&& location = std::source_location::current() +); + +/** + * Pops a task off the default logger's task stack and writes its status to the log. + * + * @param status Exit status of the task. A value of `0` or `EXIT_SUCCESS` indicates the task exited successfully. A non-zero exit status indicates the task failed. + * @param description Error code description. + */ +void pop_task +( + int status, + const std::string& description = std::string(), + std::source_location&& location = std::source_location::current() +); + +} // namespace log +} // namespace debug + +#endif // ANTKEEPER_DEBUG_LOG_HPP diff --git a/src/debug/log/event.hpp b/src/debug/log/event.hpp new file mode 100644 index 0000000..c486e50 --- /dev/null +++ b/src/debug/log/event.hpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_DEBUG_LOG_EVENT_HPP +#define ANTKEEPER_DEBUG_LOG_EVENT_HPP + +#include "debug/log/message-severity.hpp" +#include +#include +#include +#include + +namespace debug { +namespace log { + +class logger; + +/** + * Debug logging events. + */ +namespace event { + +/** + * Event generated when a message has been logged. + */ +struct message_logged +{ + /// Logger which received the message. + log::logger* logger; + + /// Time at which the message was sent. + std::chrono::time_point time; + + /// ID of the thread from which the message was sent. + std::thread::id thread_id; + + /// Source location from which the message was sent. + std::source_location location; + + /// Severity of the message. + message_severity severity; + + /// Message contents. + std::string message; +}; + +} // namespace event +} // namespace log +} // namespace debug + +#endif // ANTKEEPER_DEBUG_LOG_EVENT_HPP diff --git a/src/debug/console-commands.hpp b/src/debug/log/logger.cpp similarity index 65% rename from src/debug/console-commands.hpp rename to src/debug/log/logger.cpp index 8b56e99..8b20bb4 100644 --- a/src/debug/console-commands.hpp +++ b/src/debug/log/logger.cpp @@ -17,26 +17,28 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_DEBUG_CONSOLE_COMMANDS_HPP -#define ANTKEEPER_DEBUG_CONSOLE_COMMANDS_HPP - -#include "game/context.hpp" -#include +#include "debug/log/logger.hpp" +#include +#include namespace debug { - -/// Console commands -namespace cc { - -std::string echo(std::string text); - -std::string exit(game::context* ctx); - -std::string scrot(game::context* ctx); - -std::string cue(game::context* ctx, float t, std::string command); - -} // namespace cc +namespace log { + +void logger::log(std::string&& message, message_severity severity, std::source_location&& location) +{ + // Generate message logged event + message_logged_publisher.publish + ( + { + this, + std::chrono::system_clock::now(), + std::this_thread::get_id(), + std::move(location), + severity, + std::move(message) + } + ); +} + +} // namespace log } // namespace debug - -#endif // ANTKEEPER_DEBUG_CONSOLE_COMMANDS_HPP diff --git a/src/debug/log/logger.hpp b/src/debug/log/logger.hpp new file mode 100644 index 0000000..344cc9d --- /dev/null +++ b/src/debug/log/logger.hpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_DEBUG_LOG_LOGGER_HPP +#define ANTKEEPER_DEBUG_LOG_LOGGER_HPP + +#include "debug/log/message-severity.hpp" +#include "debug/log/event.hpp" +#include "event/publisher.hpp" +#include +#include + +namespace debug { +namespace log { + +/** + * Generates an event each time a message logged. + */ +class logger +{ +public: + /** + * Logs a message. + * + * @param message Message contents. + * @param severity Message severity. + * @param location Source location from which the message was sent. + */ + void log + ( + std::string&& message, + message_severity severity = message_severity::info, + std::source_location&& location = std::source_location::current() + ); + + /// Returns the channel through which message logged events are published. + [[nodiscard]] inline ::event::channel& get_message_logged_channel() noexcept + { + return message_logged_publisher.channel(); + } + +private: + ::event::publisher message_logged_publisher; +}; + +} // namespace log +} // namespace debug + +#endif // ANTKEEPER_DEBUG_LOG_LOGGER_HPP diff --git a/src/debug/log/message-severity.hpp b/src/debug/log/message-severity.hpp new file mode 100644 index 0000000..f70ac23 --- /dev/null +++ b/src/debug/log/message-severity.hpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_HPP +#define ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_HPP + +#include + +/// Trace message severity level. +#define ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_TRACE 0 + +/// Debug message severity level. +#define ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_DEBUG 1 + +/// Info message severity level. +#define ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_INFO 2 + +/// Warning message severity level. +#define ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_WARNING 3 + +/// Error message severity level. +#define ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_ERROR 4 + +/// Fatal error message severity level. +#define ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_FATAL 5 + +namespace debug { +namespace log { + +/// Log message severity levels. +enum class message_severity: std::uint8_t +{ + /// Trace message severity. + trace = ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_TRACE, + + /// Debug message severity. + debug = ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_DEBUG, + + /// Info message severity. + info = ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_INFO, + + /// Warning message severity. + warning = ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_WARNING, + + /// Error message severity. + error = ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_ERROR, + + /// Fatal error message severity. + fatal = ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_FATAL, +}; + +} // namespace log +} // namespace debug + +#endif // ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_HPP diff --git a/src/debug/logger.cpp b/src/debug/logger.cpp deleted file mode 100644 index fa95106..0000000 --- a/src/debug/logger.cpp +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "logger.hpp" -#include "utility/timestamp.hpp" -#include - -#if defined(_WIN32) - #define WIN32_LEAN_AND_MEAN - #include -#endif - -namespace debug { - -logger::logger(): - os(&std::cout), - auto_newline(true), - timestamp_enabled(true), - indent("| "), - log_prefix(std::string()), - log_postfix(std::string()), - warning_prefix(std::string()), - warning_postfix(std::string()), - error_prefix(std::string()), - error_postfix(std::string()), - success_prefix(std::string()), - success_postfix(std::string()) -{} - -logger::~logger() -{} - -void logger::redirect(std::ostream* stream) -{ - os = stream; -} - -void logger::log(const std::string& text) -{ - if (os) - { - std::string message = ""; - - // Prepend timestamp - if (timestamp_enabled) - { - message += timestamp(); - message += ": "; - } - - // Prepend indentation - for (std::size_t i = 0; i < tasks.size(); ++i) - message += indent; - - // Append text - message += (log_prefix + text + log_postfix); - - // Append newline - if (auto_newline) - message += "\n"; - - // Add message to log history - const std::lock_guard history_lock(history_mutex); - history += message; - - // Output message - (*os) << message; - - // Flush output stream - os->flush(); - } -} - -void logger::warning(const std::string& text) -{ - #if defined(_WIN32) - SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN); - #endif - - log(warning_prefix + text + warning_postfix); - - #if defined(_WIN32) - SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); - #endif -} - -void logger::error(const std::string& text) -{ - #if defined(_WIN32) - SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED); - #endif - - log(error_prefix + text + error_postfix); - - #if defined(_WIN32) - SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); - #endif -} - -void logger::success(const std::string& text) -{ - /* - #if defined(_WIN32) - SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN); - #endif - */ - - log(success_prefix + text + success_postfix); - - /* - #if defined(_WIN32) - SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); - #endif - */ -} - -void logger::set_auto_newline(bool enabled) -{ - auto_newline = enabled; -} - -void logger::set_timestamp(bool enabled) -{ - timestamp_enabled = enabled; -} - -void logger::set_indent(const std::string& indent) -{ - this->indent = indent; -} - -void logger::set_log_prefix(const std::string& prefix) -{ - log_prefix = prefix; -} - -void logger::set_log_postfix(const std::string& postfix) -{ - log_postfix = postfix; -} - -void logger::set_warning_prefix(const std::string& prefix) -{ - warning_prefix = prefix; -} - -void logger::set_warning_postfix(const std::string& postfix) -{ - warning_postfix = postfix; -} - -void logger::set_error_prefix(const std::string& prefix) -{ - error_prefix = prefix; -} - -void logger::set_error_postfix(const std::string& postfix) -{ - error_postfix = postfix; -} - -void logger::set_success_prefix(const std::string& prefix) -{ - success_prefix = prefix; -} - -void logger::set_success_postfix(const std::string& postfix) -{ - success_postfix = postfix; -} - -void logger::push_task(const std::string& description) -{ - std::string message = description + " {"; - if (!auto_newline) - message += "\n"; - - log(message); - - tasks.push(description); -} - -void logger::pop_task(int status, std::string error) -{ - if (tasks.empty()) - { - return; - } - - std::string message = "} => "; - - tasks.pop(); - - if (status == EXIT_SUCCESS) - { - message += "success"; - if (!auto_newline) - message += "\n"; - - success(message); - } - else - { - message += "failure"; - if (!error.empty()) - message += " (" + error + ")"; - if (!auto_newline) - message += "\n"; - - this->error(message); - } -} - -} // namespace debug diff --git a/src/debug/logger.hpp b/src/debug/logger.hpp deleted file mode 100644 index 239d4d2..0000000 --- a/src/debug/logger.hpp +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_DEBUG_LOGGER_HPP -#define ANTKEEPER_DEBUG_LOGGER_HPP - -#include -#include -#include -#include -#include - -namespace debug { - -/** - * Logs formatted debug messages to an output stream. - */ -class logger -{ -public: - /// Creates a logger. - logger(); - - /// Destroys a logger. - ~logger(); - - /** - * Redirects log output to the specified output stream. - * - * @param stream Output stream to which log text will be written. - */ - void redirect(std::ostream* stream); - /** - * Outputs text to the log. - */ - void log(const std::string& text); - - void warning(const std::string& text); - void error(const std::string& text); - void success(const std::string& text); - - /** - * Enables or disables automatic appending of newlines to log messages. - * - * @param enabled `true` if auto newline should be enabled, `false` otherwise. - */ - void set_auto_newline(bool enabled); - - /** - * Enables or disables prefixing log messages with a timestamp. - * - * @param enabled `true` if timestamps should be enabled, `false` otherwise. - */ - void set_timestamp(bool enabled); - - /** - * Sets the indent string which prefixes subtasks. This string will be repeated according to the level of indentation. - * - * @param indent Indent string. - */ - void set_indent(const std::string& indent); - - void set_log_prefix(const std::string& prefix); - void set_log_postfix(const std::string& postfix); - void set_warning_prefix(const std::string& prefix); - void set_warning_postfix(const std::string& postfix); - void set_error_prefix(const std::string& prefix); - void set_error_postfix(const std::string& postfix); - void set_success_prefix(const std::string& prefix); - void set_success_postfix(const std::string& postfix); - - /** - * Pushes a task onto the stack and outputs it to the log. - * - * @param description Task description. - */ - void push_task(const std::string& description); - - /** - * Pops a task off the stack and outputs its status to the log. - * - * @param status Exit status of the task. A value of `0` or `EXIT_SUCCESS` indicates the task exited successfully. A non-zero exit status indicates the task failed. - */ - void pop_task(int status, std::string error = std::string()); - - const std::string& get_history() const; - -private: - std::ostream* os; - bool auto_newline; - bool timestamp_enabled; - std::string indent; - std::string log_prefix; - std::string log_postfix; - std::string warning_prefix; - std::string warning_postfix; - std::string error_prefix; - std::string error_postfix; - std::string success_prefix; - std::string success_postfix; - std::stack tasks; - std::string history; - std::mutex history_mutex; -}; - -inline const std::string& logger::get_history() const -{ - return history; -} - -} // namespace debug - -#endif // ANTKEEPER_DEBUG_LOGGER_HPP - diff --git a/src/debug/performance-sampler.hpp b/src/debug/performance-sampler.hpp index 3c0ff75..5614b5e 100644 --- a/src/debug/performance-sampler.hpp +++ b/src/debug/performance-sampler.hpp @@ -56,7 +56,7 @@ public: /** * Returns the mean frame duration. */ - double mean_frame_duration() const; + [[nodiscard]] double mean_frame_duration() const; private: std::vector samples; diff --git a/src/event/channel.hpp b/src/event/channel.hpp new file mode 100644 index 0000000..aa5c26e --- /dev/null +++ b/src/event/channel.hpp @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_EVENT_CHANNEL_HPP +#define ANTKEEPER_EVENT_CHANNEL_HPP + +#include +#include +#include +#include +#include "event/subscriber.hpp" +#include "event/subscription.hpp" +#include "event/queue.hpp" + +namespace event { + +template +class publisher; + +/** + * Channel through which messages are published. + * + * @tparam T Message type. + */ +template +class channel +{ +public: + /// Message type. + typedef T message_type; + + /// Subscriber function object type. + typedef subscriber subscriber_type; + + /** + * Subscribes a function object to messages published through this channel. + * + * @param subscriber Subscriber function object which will received published messages. + * + * @return Shared subscription object which will unsubscribe the subscriber on destruction. + */ + [[nodiscard]] std::shared_ptr subscribe(subscriber_type&& subscriber) + { + // Construct shared pointer to subscriber function + std::shared_ptr shared_subscriber = std::make_shared(std::move(subscriber)); + + // Append subscriber to subscriber list and store iterator + auto iterator = subscribers.insert(subscribers.end(), shared_subscriber); + + // Construct and return a shared subscription object which removes the subscriber from the subscriber list when unsubscribed or destructed + return std::make_shared + ( + std::static_pointer_cast(shared_subscriber), + [this, iterator = std::move(iterator)] + { + this->subscribers.erase(iterator); + } + ); + } + + /** + * Subscribes a message queue to messages published through this channel. + * + * @param queue Message queue which will received published messages. + * + * @return Shared subscription object which will unsubscribe the queue on destruction. + */ + [[nodiscard]] std::shared_ptr subscribe(event::queue& queue) + { + return subscribe + ( + [&queue](const message_type& message) + { + queue.enqueue(message); + } + ); + } + +private: + friend class publisher; + + /// List of subscribers. + std::list> subscribers; +}; + +} // namespace event + +#endif // ANTKEEPER_EVENT_CHANNEL_HPP diff --git a/src/event/event-dispatcher.cpp b/src/event/event-dispatcher.cpp deleted file mode 100644 index c798c6c..0000000 --- a/src/event/event-dispatcher.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "event-dispatcher.hpp" - -event_dispatcher::event_dispatcher(): - updating(false) -{} - -event_dispatcher::~event_dispatcher() -{ - clear(); -} - -void event_dispatcher::update(double time) -{ - updating = true; - - // Process pending subscriptions - for (auto it = to_subscribe.begin(); it != to_subscribe.end(); ++it) - { - handler_map[std::get<0>(*it)].push_back(std::get<1>(*it)); - } - to_subscribe.clear(); - - // Process pending unsubscriptions - for (auto it = to_unsubscribe.begin(); it != to_unsubscribe.end(); ++it) - { - handler_map[std::get<0>(*it)].remove(std::get<1>(*it)); - } - to_unsubscribe.clear(); - - // Dispatch queued events - flush(); - - // For each scheduled event - for (auto event = scheduled_events.begin(); event != scheduled_events.end();) - { - // If the event is due - if (time >= event->first) - { - // Dispatch event - dispatch(*(event->second)); - - // Delete event - delete event->second; - event = scheduled_events.erase(event); - } - else - { - break; - } - } - - updating = false; -} - -void event_dispatcher::dispatch(const event_base& event) -{ - // Get list of handlers for this type of event - const std::list& handlers = handler_map[event.get_event_type_id()]; - - // For each handler - for (auto handler = handlers.begin(); handler != handlers.end(); ++handler) - { - // Pass event to the handler - (*handler)->route_event(event); - } -} - -void event_dispatcher::flush() -{ - // For each event in the queue - for (auto event = queued_events.begin(); event != queued_events.end(); ++event) - { - // Dispatch event - dispatch(**event); - - // Delete event - delete (*event); - } - - // Clear event queue - queued_events.clear(); -} - -void event_dispatcher::clear() -{ - // For each event in the queue - for (auto event = queued_events.begin(); event != queued_events.end(); ++event) - { - // Delete event - delete (*event); - } - - // Clear event queue - queued_events.clear(); - - // For each scheduled event - for (auto event = scheduled_events.begin(); event != scheduled_events.end(); ++event) - { - // Delete event - delete event->second; - } - - // Clear scheduled events - scheduled_events.clear(); -} - diff --git a/src/event/event-dispatcher.hpp b/src/event/event-dispatcher.hpp deleted file mode 100644 index 866835c..0000000 --- a/src/event/event-dispatcher.hpp +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_EVENT_DISPATCHER_HPP -#define ANTKEEPER_EVENT_DISPATCHER_HPP - -#include "event.hpp" -#include "event-handler.hpp" -#include -#include -#include - -/** - * Queues events and dispatches them to event handlers. - */ -class event_dispatcher -{ -public: - /// Creates an event dispatcher - event_dispatcher(); - - /// Destroys an event dispatcher - ~event_dispatcher(); - - /** - * Processes all pending subscriptions and unsubscriptions, dispatches queued events, then dispatches due scheduled events. - * - * @param time The current time. - */ - void update(double time); - - /** - * Subscribes an event handler to event dispatches. - * - * @param handler Handler to subscribe. - */ - template - void subscribe(event_handler* handler); - - /** - * Unsubscribes an event handler from event dispatches. - * - * @param handler Handler to unsubscribe. - */ - template - void unsubscribe(event_handler* handler); - - /** - * Adds an event to the queue. - * - * @param event Event to queue. - */ - void queue(const event_base& event); - - /** - * Schedules an event to be dispatched at a specific time. - * - * @param event Event to schedule. - * @param time Time that the event should be dispatched. - */ - void schedule(const event_base& event, double time); - - /** - * Dispatches a single event. - * - * @param event Event to dispatch. - */ - void dispatch(const event_base& event); - - /** - * Dispatches all events in the queue. - */ - void flush(); - - /// Removes all queued and scheduled events from the queue without notifying handlers. - void clear(); - -private: - std::list> to_subscribe; - std::list> to_unsubscribe; - std::map> handler_map; - std::list queued_events; - std::multimap scheduled_events; - bool updating; -}; - -template -void event_dispatcher::subscribe(event_handler* handler) -{ - if (updating) - { - to_subscribe.push_back(std::make_tuple(handler->get_handled_event_type_id(), handler)); - } - else - { - handler_map[handler->get_handled_event_type_id()].push_back(handler); - } -} - -template -void event_dispatcher::unsubscribe(event_handler* handler) -{ - if (updating) - { - to_unsubscribe.push_back(std::make_tuple(handler->get_handled_event_type_id(), handler)); - } - else - { - handler_map[handler->get_handled_event_type_id()].remove(handler); - } -} - -inline void event_dispatcher::queue(const event_base& event) -{ - queued_events.push_back(event.clone()); -} - -inline void event_dispatcher::schedule(const event_base& event, double time) -{ - scheduled_events.insert(std::pair(time, event.clone())); -} - -#endif // ANTKEEPER_EVENT_DISPATCHER_HPP - diff --git a/src/event/event-handler.hpp b/src/event/event-handler.hpp deleted file mode 100644 index 5579971..0000000 --- a/src/event/event-handler.hpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_EVENT_HANDLER_HPP -#define ANTKEEPER_EVENT_HANDLER_HPP - -#include "event.hpp" -#include - -class event_dispatcher; - -/** - * Abstract base class for event handlers. - */ -class event_handler_base -{ -private: - friend class event_dispatcher; - - /** - * Receives an event, casts it to its derived event type, then handles it. - * - * @param event Received event. - */ - virtual void route_event(const event_base& event) = 0; -}; - -/** - * Templates abstract base class for event handlers. - * - * @tparam Event type. - */ -template -class event_handler: public event_handler_base -{ -public: - static_assert(std::is_base_of::value, "T must be a descendant of event_base."); - - /// Returns the unique event type identifier for the event type handled by this event handler. - const std::size_t get_handled_event_type_id() const; - - /** - * Handles an event of type T. - * - * @param event Event to handle. - */ - virtual void handle_event(const T& event) = 0; - -private: - /// @copydoc event_handler_base::route_event() - virtual void route_event(const event_base& event) final; -}; - -template -inline const std::size_t event_handler::get_handled_event_type_id() const -{ - return T::event_type_id; -} - -template -void event_handler::route_event(const event_base& event) -{ - handle_event(static_cast(event)); -} - -#endif // ANTKEEPER_EVENT_HANDLER_HPP - diff --git a/src/event/event.hpp b/src/event/event.hpp deleted file mode 100644 index b6da8f9..0000000 --- a/src/event/event.hpp +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_EVENT_HPP -#define ANTKEEPER_EVENT_HPP - -#include -#include - -/** - * Abstract base class for events. - */ -class event_base -{ -public: - /// Destroys an event base. - virtual ~event_base() = default; - - /// Returns the unique event type identifier for this event type. - virtual const std::size_t get_event_type_id() const = 0; - - /** - * Allocates a copy of this event. - * - * @return Newly allocated copy of this event. - */ - virtual event_base* clone() const = 0; - -protected: - /// Returns then increments the next available event type ID. - static std::size_t next_event_type_id(); -}; - -inline std::size_t event_base::next_event_type_id() -{ - static std::atomic next_event_type_id{0}; - return next_event_type_id++; -} - -/** - * Templated abstract base class for events. - * - * @tparam T The derived class. - */ -template -class event: public event_base -{ -public: - /// The unique event type identifier for this event type. - static const std::atomic event_type_id; - - /// Destroys an event - virtual ~event() = default; - - /// @copydoc event_base::get_event_type_id() const - virtual const std::size_t get_event_type_id() const final; - - /// @copydoc event_base::clone() const - virtual event_base* clone() const = 0; -}; - -template -const std::atomic event::event_type_id{event_base::next_event_type_id()}; - -template -inline const std::size_t event::get_event_type_id() const -{ - return event_type_id; -} - -#endif // ANTKEEPER_EVENT_HPP - diff --git a/src/event/input-events.cpp b/src/event/input-events.cpp deleted file mode 100644 index 71175de..0000000 --- a/src/event/input-events.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "input-events.hpp" - -event_base* key_pressed_event::clone() const -{ - key_pressed_event* event = new key_pressed_event(); - event->keyboard = keyboard; - event->scancode = scancode; - return event; -} - -event_base* key_released_event::clone() const -{ - key_released_event* event = new key_released_event(); - event->keyboard = keyboard; - event->scancode = scancode; - return event; -} - -event_base* mouse_moved_event::clone() const -{ - mouse_moved_event* event = new mouse_moved_event(); - event->mouse = mouse; - event->x = x; - event->y = y; - event->dx = dx; - event->dy = dy; - return event; -} - -event_base* mouse_button_pressed_event::clone() const -{ - mouse_button_pressed_event* event = new mouse_button_pressed_event(); - event->mouse = mouse; - event->button = button; - event->x = x; - event->y = y; - return event; -} - -event_base* mouse_button_released_event::clone() const -{ - mouse_button_released_event* event = new mouse_button_released_event(); - event->mouse = mouse; - event->button = button; - event->x = x; - event->y = y; - return event; -} - -event_base* mouse_wheel_scrolled_event::clone() const -{ - mouse_wheel_scrolled_event* event = new mouse_wheel_scrolled_event(); - event->mouse = mouse; - event->x = x; - event->y = y; - return event; -} - -event_base* gamepad_connected_event::clone() const -{ - gamepad_connected_event* event = new gamepad_connected_event(); - event->controller = controller; - event->reconnected = reconnected; - return event; -} - -event_base* gamepad_disconnected_event::clone() const -{ - gamepad_disconnected_event* event = new gamepad_disconnected_event(); - event->controller = controller; - return event; -} - -event_base* gamepad_button_pressed_event::clone() const -{ - gamepad_button_pressed_event* event = new gamepad_button_pressed_event(); - event->controller = controller; - event->button = button; - return event; -} - -event_base* gamepad_button_released_event::clone() const -{ - gamepad_button_released_event* event = new gamepad_button_released_event(); - event->controller = controller; - event->button = button; - return event; -} - -event_base* gamepad_axis_moved_event::clone() const -{ - gamepad_axis_moved_event* event = new gamepad_axis_moved_event(); - event->controller = controller; - event->axis = axis; - event->value = value; - return event; -} - diff --git a/src/event/input-events.hpp b/src/event/input-events.hpp deleted file mode 100644 index a9e3c41..0000000 --- a/src/event/input-events.hpp +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_INPUT_EVENTS_HPP -#define ANTKEEPER_INPUT_EVENTS_HPP - -#include "event/event.hpp" -#include "input/scancode.hpp" -#include "input/keyboard.hpp" -#include "input/mouse.hpp" -#include "input/gamepad.hpp" - -/** - * Input event which indicates a keyboard key has been pressed. - */ -class key_pressed_event: public event -{ -public: - virtual event_base* clone() const; - - input::keyboard* keyboard; - input::scancode scancode; -}; - -/** - * Input event which indicates a keyboard key has been released. - */ -class key_released_event: public event -{ -public: - virtual event_base* clone() const; - - input::keyboard* keyboard; - input::scancode scancode; -}; - -/** - * Input event which indicates a mouse has been moved. - */ -class mouse_moved_event: public event -{ -public: - virtual event_base* clone() const; - - input::mouse* mouse; - int x; - int y; - int dx; - int dy; -}; - -/** - * Input event which indicates a mouse button has been pressed. - */ -class mouse_button_pressed_event: public event -{ -public: - virtual event_base* clone() const; - - input::mouse* mouse; - int button; - int x; - int y; -}; - -/** - * Input event which indicates a mouse button has been released. - */ -class mouse_button_released_event: public event -{ -public: - virtual event_base* clone() const; - - input::mouse* mouse; - int button; - int x; - int y; -}; - -/** - * Input event which indicates a mouse wheel has been scrolled. - */ -class mouse_wheel_scrolled_event: public event -{ -public: - virtual event_base* clone() const; - - input::mouse* mouse; - int x; - int y; -}; - -/** - * Input event which indicates a controller has been connected. - */ -class gamepad_connected_event: public event -{ -public: - virtual event_base* clone() const; - - input::gamepad* controller; - bool reconnected; -}; - -/** - * Input event which indicates a controller has been disconnected. - */ -class gamepad_disconnected_event: public event -{ -public: - virtual event_base* clone() const; - - input::gamepad* controller; -}; - -/** - * Input event which indicates a controller button has been pressed. - */ -class gamepad_button_pressed_event: public event -{ -public: - virtual event_base* clone() const; - - input::gamepad* controller; - input::gamepad_button button; -}; - -/** - * Input event which indicates a controller button has been released. - */ -class gamepad_button_released_event: public event -{ -public: - virtual event_base* clone() const; - - input::gamepad* controller; - input::gamepad_button button; -}; - -/** - * Input event which indicates a controller axis has been moved. - */ -class gamepad_axis_moved_event: public event -{ -public: - virtual event_base* clone() const; - - input::gamepad* controller; - input::gamepad_axis axis; - float value; -}; - -#endif // ANTKEEPER_INPUT_EVENTS_HPP - diff --git a/src/event/publisher.hpp b/src/event/publisher.hpp new file mode 100644 index 0000000..fcd8f2a --- /dev/null +++ b/src/event/publisher.hpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_EVENT_PUBLISHER_HPP +#define ANTKEEPER_EVENT_PUBLISHER_HPP + +#include +#include +#include "event/channel.hpp" + +namespace event { + +/** + * Publishes messages to subscribers. + * + * @tparam T Message type. + */ +template +class publisher +{ +public: + /// Message type. + typedef T message_type; + + /// Channel type. + typedef channel channel_type; + + /** + * Publishes a message. + * + * @tparam ExecutionPolicy Execution policy type. + * + * @param policy Execution policy to use. + * @param message Message to publish. + */ + /// @{ + template + void publish(ExecutionPolicy&& policy, const message_type& message) const + { + std::for_each + ( + policy, + std::begin(m_channel.subscribers), + std::end(m_channel.subscribers), + [&](const auto& subscriber) + { + (*subscriber)(message); + } + ); + } + + void publish(const message_type& message) const + { + publish(std::execution::seq, message); + } + /// @} + + /** + * Returns the channel through which messages are published. + */ + /// @{ + [[nodiscard]] inline const channel_type& channel() const noexcept + { + return m_channel; + } + + [[nodiscard]] inline channel_type& channel() noexcept + { + return m_channel; + } + /// @} + +private: + channel_type m_channel; +}; + +} // namespace event + +#endif // ANTKEEPER_EVENT_PUBLISHER_HPP diff --git a/src/event/queue.hpp b/src/event/queue.hpp new file mode 100644 index 0000000..4ae753e --- /dev/null +++ b/src/event/queue.hpp @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_EVENT_QUEUE_HPP +#define ANTKEEPER_EVENT_QUEUE_HPP + +#include +#include +#include +#include +#include +#include +#include "event/subscriber.hpp" +#include "event/subscription.hpp" +#include "utility/type-id.hpp" + +namespace event { + +/** + * Collects messages from publishers to be distributed to subscribers when desired. + */ +class queue +{ +public: + /** + * Subscribes a function object to messages published by this queue. + * + * @tparam T Message type. + * + * @param subscriber Function object to subscribe. + * + * @return Shared subscription object which will unsubscribe the subscriber on destruction. + */ + template + [[nodiscard]] std::shared_ptr subscribe(subscriber&& subscriber) + { + // Allocate shared pointer to std::any object containing subscriber + std::shared_ptr shared_subscriber = std::make_shared(std::make_any>(std::move(subscriber))); + + // Append subscriber to subscriber list and store iterator + auto iterator = subscribers.emplace(type_id, shared_subscriber); + + // Construct and return a shared subscription object which removes the subscriber from the subscriber list when unsubscribed or destructed + return std::make_shared + ( + std::static_pointer_cast(shared_subscriber), + [this, iterator = std::move(iterator)]() + { + this->subscribers.erase(iterator); + } + ); + } + + /** + * Adds a message to the queue, to be distributed later. + * + * @tparam T Message type. + * + * @param message Message to enqueue. + */ + template + void enqueue(const T& message) + { + messages.emplace_back + ( + [this, message]() + { + this->distribute(message); + } + ); + } + + /** + * Distributes queued messages in FIFO order to subscribers. + */ + void flush() + { + while (!messages.empty()) + { + messages.front()(); + messages.pop_front(); + } + } + + /** + * Removes all messages from the queue. + */ + void clear() + { + messages.clear(); + } + + /** + * Returns `true` if there are no messages in the queue, `false` otherwise. + */ + [[nodiscard]] inline bool empty() const noexcept + { + return messages.empty(); + } + +private: + /** + * Distributes a message. + * + * @tparam T Message type. + * + * @param message Message to distribute. + */ + template + void distribute(const T& message) const + { + // For each subscriber of the given message type + const auto range = subscribers.equal_range(type_id); + for (auto i = range.first; i != range.second; ++i) + { + // Send message to subscriber + std::any_cast>(*(i->second))(message); + } + } + + std::multimap> subscribers; + std::list> messages; +}; + +} // namespace event + +#endif // ANTKEEPER_EVENT_QUEUE_HPP diff --git a/src/event/signal.hpp b/src/event/signal.hpp deleted file mode 100644 index 339e901..0000000 --- a/src/event/signal.hpp +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_EVENT_SIGNAL_HPP -#define ANTKEEPER_EVENT_SIGNAL_HPP - -#include -#include -#include -#include -#include -#include - -//namespace event { - -template -class signal; - -template -class connector; - -/** - * Manages a connection between a signal and handler. A signal will be disconnected from a handler when the connection is destructed or is disconnected manually via connection::disconnect(). - */ -class connection -{ -public: - /// Signal handler disconnect function type. - typedef std::function)> disconnector_type; - - /** - * Constructs a connection between a signal and a handler. - * - * @param handler Weak pointer to a signal handler. - * @param disconnector Signal handler disconnect function. - */ - connection(std::weak_ptr handler, disconnector_type disconnector); - - /** - * Destructs a connection between a signal and a handler. - */ - ~connection(); - - /** - * Returns `true` if the signal and handler are connected, `false` otherwise. - */ - bool connected() const noexcept; - - /** - * Disconnects the signal from the handler. - */ - void disconnect(); - -private: - template - friend class signal; - - std::weak_ptr handler; - disconnector_type disconnector; -}; - -/** - * Creates connections between a signal and signal handlers. - * - * @tparam T Signal response type. - * @tparam Args Signal argument types. - */ -template -class connector -{ -public: - /// Signal response type. - typedef T response_type; - - /// Signal type. - typedef signal signal_type; - - /** - * Constructs a signal connector. - * - * @param signal Signal to which handlers may be connected. - */ - connector(signal_type& signal): - signal(&signal) - {} - - /// @copydoc signal::connect(handler_type) - std::shared_ptr connect(typename signal_type::handler_type handler) - { - return signal->connect(handler); - } - -private: - signal_type* signal; -}; - -/** - * Emits signals to signal handlers. - * - * @tparam T Signal response type. - * @tparam Args Signal argument types. - */ -template -class signal -{ -public: - /// Signal response type. - typedef T response_type; - - /// Signal handler type. - typedef std::function handler_type; - - /// Signal connector type. - typedef connector connector_type; - - /** - * Constructs a signal. - */ - signal(): - signal_connector(*this) - {} - - /** - * Returns the connector for this signal. - */ - connector_type& connector() noexcept - { - return signal_connector; - } - - /** - * Connects the signal to a handler. - * - * @param handler Signal handler to connect. - * - * @return Connection between the signal and handler. - */ - std::shared_ptr connect(handler_type handler) - { - // Allocate shared pointer to handler - std::shared_ptr shared_handler = std::make_shared(handler); - - // Add handler to list of connected handlers - connections.push_back(shared_handler); - - // Return a shared pointer to the connection between the signal and handler - return std::make_shared - ( - std::static_pointer_cast(shared_handler), - [this](std::weak_ptr handler) - { - this->connections.remove(std::static_pointer_cast(handler.lock())); - } - ); - } - - /** - * Disconnects the signal from all connected handlers. - */ - void disconnect() - { - connections.clear(); - } - - /** - * Emits a signal to all connected handlers. - * - * @tparam ExecutionPolicy Execution policy type. - * - * @param policy Execution policy to use. - * @param args Signal arguments. - */ - /// @{ - template - void emit(ExecutionPolicy&& policy, Args... args) const - { - std::for_each - ( - policy, - std::begin(connections), - std::end(connections), - [&](const auto& handler) - { - (*handler)(args...); - } - ); - } - - void emit(Args... args) const - { - emit(std::execution::seq, args...); - } - /// @} - - /** - * Emits a signal to all connected handlers and relays their responses to a listener. - * - * @tparam ExecutionPolicy Execution policy type. - * @tparam UnaryFunction Listener function object type. - * - * @param policy Execution policy to use. - * @param listener Listener function object. - * @param args Signal arguments. - */ - /// @{ - template - void ping(ExecutionPolicy&& policy, UnaryFunction listener, Args... args) const - { - std::for_each - ( - policy, - std::begin(connections), - std::end(connections), - [&](const auto& handler) - { - if constexpr(std::is_void_v) - { - (*handler)(args...); - listener(); - } - else - { - listener((*handler)(args...)); - } - } - ); - } - - template - void ping(UnaryFunction listener, Args... args) const - { - ping(std::execution::seq, listener, args...); - } - /// @} - -private: - /// List of connected signal handlers. - std::list> connections; - - /// Signal connector. - connector_type signal_connector; -}; - -//} // namespace event - -#endif // ANTKEEPER_EVENT_SIGNAL_HPP diff --git a/src/event/window-events.hpp b/src/event/subscriber.hpp similarity index 70% rename from src/event/window-events.hpp rename to src/event/subscriber.hpp index 212205c..2d0bdfd 100644 --- a/src/event/window-events.hpp +++ b/src/event/subscriber.hpp @@ -17,22 +17,21 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_WINDOW_EVENTS_HPP -#define ANTKEEPER_WINDOW_EVENTS_HPP +#ifndef ANTKEEPER_EVENT_SUBSCRIBER_HPP +#define ANTKEEPER_EVENT_SUBSCRIBER_HPP -#include "event/event.hpp" +#include + +namespace event { /** - * Input event which indicates a mouse button has been pressed. + * Subscriber function object type. + * + * @tparam T Message type. */ -class window_resized_event: public event -{ -public: - virtual event_base* clone() const; - - int w; - int h; -}; +template +using subscriber = std::function; -#endif // ANTKEEPER_WINDOW_EVENTS_HPP +} // namespace event +#endif // ANTKEEPER_EVENT_SUBSCRIBER_HPP diff --git a/src/event/subscription.cpp b/src/event/subscription.cpp new file mode 100644 index 0000000..a772dfb --- /dev/null +++ b/src/event/subscription.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "event/subscription.hpp" +#include + +namespace event { + +subscription::subscription(std::weak_ptr&& subscriber, unsubscribe_type&& unsubscriber): + subscriber(std::move(subscriber)), + unsubscriber(std::move(unsubscriber)) +{} + +subscription::~subscription() +{ + unsubscribe(); +} + +bool subscription::expired() const noexcept +{ + return subscriber.expired(); +} + +void subscription::unsubscribe() +{ + if (!expired()) + { + unsubscriber(); + } +} + +} // namespace event diff --git a/src/event/subscription.hpp b/src/event/subscription.hpp new file mode 100644 index 0000000..71911d3 --- /dev/null +++ b/src/event/subscription.hpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_EVENT_SUBSCRIPTION_HPP +#define ANTKEEPER_EVENT_SUBSCRIPTION_HPP + +#include +#include + +namespace event { + +/** + * Subscription object which unsubscribes on destruction. + */ +class subscription +{ +public: + /// Unsubscribe function object type. + typedef std::function unsubscribe_type; + + /** + * Constructs a subscription. + * + * @param subscriber Weak pointer to the subscriber. + * @param unsubscriber Unsubscribe function object. + */ + subscription(std::weak_ptr&& subscriber, unsubscribe_type&& unsubscriber); + + /** + * Unsubscribes the subscriber and destructs the subscription. + */ + ~subscription(); + + /** + * Returns `true` if the subscription is no longer active, `false` otherwise. + */ + [[nodiscard]] bool expired() const noexcept; + + /** + * Unsubscribes the subscriber. + */ + void unsubscribe(); + +private: + std::weak_ptr subscriber; + unsubscribe_type unsubscriber; +}; + +} // namespace event + +#endif // ANTKEEPER_EVENT_SUBSCRIPTION_HPP diff --git a/src/game/ant/morphogenesis.cpp b/src/game/ant/morphogenesis.cpp index ed262d3..a1f9e8d 100644 --- a/src/game/ant/morphogenesis.cpp +++ b/src/game/ant/morphogenesis.cpp @@ -23,7 +23,6 @@ #include "math/transform-operators.hpp" #include "math/quaternion.hpp" #include -#include namespace game { namespace ant { diff --git a/src/game/context.hpp b/src/game/context.hpp index daf0f20..5fd91ca 100644 --- a/src/game/context.hpp +++ b/src/game/context.hpp @@ -20,47 +20,45 @@ #ifndef ANTKEEPER_GAME_CONTEXT_HPP #define ANTKEEPER_GAME_CONTEXT_HPP -#include "utility/fundamental-types.hpp" -#include "resources/string-table.hpp" +#include "animation/tween.hpp" +#include "application.hpp" +#include "debug/performance-sampler.hpp" #include "entity/id.hpp" #include "entity/registry.hpp" +#include "event/subscription.hpp" +#include "game/ecoregion.hpp" +#include "game/loop.hpp" +#include "game/state/base.hpp" #include "geom/aabb.hpp" +#include "gl/framebuffer.hpp" +#include "gl/rasterizer.hpp" +#include "gl/texture-2d.hpp" #include "gl/vertex-array.hpp" #include "gl/vertex-buffer.hpp" -#include "gl/texture-2d.hpp" -#include "gl/rasterizer.hpp" -#include "gl/framebuffer.hpp" +#include "input/control-map.hpp" #include "input/control.hpp" -#include "input/control-set.hpp" -#include "input/listener.hpp" #include "input/mapper.hpp" -#include "input/event-router.hpp" -#include "animation/tween.hpp" +#include "render/anti-aliasing-method.hpp" +#include "render/material-property.hpp" +#include "render/material.hpp" +#include "resources/json.hpp" +#include "resources/string-table.hpp" #include "scene/scene.hpp" -#include +#include "state-machine.hpp" +#include "type/bitmap-font.hpp" +#include "type/typeface.hpp" +#include "utility/fundamental-types.hpp" +#include +#include #include -#include +#include +#include +#include +#include #include #include #include #include -#include -#include "resources/json.hpp" -#include "type/typeface.hpp" -#include "type/bitmap-font.hpp" -#include "render/material.hpp" -#include "render/material-property.hpp" -#include "render/anti-aliasing-method.hpp" -#include "ui/mouse-tracker.hpp" -#include "application.hpp" -#include "game/state/base.hpp" -#include "game/loop.hpp" -#include "game/ecoregion.hpp" -#include "state-machine.hpp" -#include "debug/performance-sampler.hpp" -#include -#include -#include // Forward declarations class animator; @@ -75,7 +73,6 @@ template class animation; namespace debug { class cli; - class logger; } namespace game @@ -129,8 +126,6 @@ struct context std::function resume_callback; /// Debugging - debug::logger* logger; - std::ofstream log_filestream; debug::performance_sampler performance_sampler; debug::cli* cli; @@ -144,10 +139,22 @@ struct context application* app; // Controls - input::event_router* input_event_router; - input::mapper* input_mapper; - input::listener* input_listener; - std::unordered_map controls; + input::mapper input_mapper; + std::forward_list> control_subscriptions; + + input::control_map window_controls; + input::control fullscreen_control; + input::control screenshot_control; + + input::control_map menu_controls; + input::control menu_up_control; + input::control menu_down_control; + input::control menu_left_control; + input::control menu_right_control; + input::control menu_select_control; + input::control menu_back_control; + input::control menu_modifier_control; + bool mouse_look; /// Game loop @@ -232,7 +239,6 @@ struct context scene::billboard* camera_flash_billboard; float font_size; bool dyslexia_font; - ui::mouse_tracker* menu_mouse_tracker; std::vector> menu_select_callbacks; std::vector> menu_left_callbacks; std::vector> menu_right_callbacks; @@ -307,7 +313,7 @@ struct context bool bloom_enabled; render::anti_aliasing_method anti_aliasing_method; - std::shared_ptr ui_resize_connection; + std::shared_ptr<::event::subscription> ui_resize_subscription; }; } // namespace game diff --git a/src/game/controls.cpp b/src/game/controls.cpp index 6d3b5ec..94ff2dc 100644 --- a/src/game/controls.cpp +++ b/src/game/controls.cpp @@ -30,7 +30,7 @@ namespace game { std::filesystem::path gamepad_calibration_path(const game::context& ctx, const input::gamepad& gamepad) { - return std::filesystem::path("gamepad-" + gamepad.get_guid() + ".json"); + return std::filesystem::path("gamepad-" + gamepad.get_uuid().string() + ".json"); } json default_control_profile() @@ -81,6 +81,7 @@ json* load_gamepad_calibration(game::context& ctx, const input::gamepad& gamepad bool save_gamepad_calibration(const game::context& ctx, const input::gamepad& gamepad, const json& calibration) { + /* // Determine absolute path to gamepad calibration file std::filesystem::path path = ctx.controls_path / gamepad_calibration_path(ctx, gamepad); @@ -100,12 +101,14 @@ bool save_gamepad_calibration(const game::context& ctx, const input::gamepad& ga // Close calibration file stream.close(); + */ return true; } void apply_control_profile(game::context& ctx, const json& profile) { + /* // Map gamepad buttons to strings const std::unordered_map gamepad_button_map = { @@ -144,21 +147,21 @@ void apply_control_profile(game::context& ctx, const json& profile) } // Get keyboard and mouse devices - input::keyboard* keyboard = ctx.app->get_keyboard(); - input::mouse* mouse = ctx.app->get_mouse(); + input::keyboard* keyboard = ctx.app->get_device_manager().get_keyboards().front(); + input::mouse* mouse = ctx.app->get_device_manager().get_mice().front(); // Find profile gamepad device input::gamepad* gamepad = nullptr; auto gamepad_element = profile.find("gamepad"); if (gamepad_element != profile.end()) { - // Get gamepad GUID - const std::string gamepad_guid = gamepad_element->get(); + // Get gamepad UUID string + const std::string uuid_string = gamepad_element->get(); - // Find gamepad with matching GUID - for (input::gamepad* device: ctx.app->get_gamepads()) + // Find gamepad with matching UUID + for (input::gamepad* device: ctx.app->get_device_manager().get_gamepads()) { - if (device->get_guid() == gamepad_guid) + if (device->get_uuid().string() == uuid_string) { gamepad = device; break; @@ -193,7 +196,7 @@ void apply_control_profile(game::context& ctx, const json& profile) { if (!mapping_element->contains("device")) { - ctx.logger->warning("Control \"" + control_name + "\" not mapped to a device"); + debug::log::warning("Control \"" + control_name + "\" not mapped to a device"); continue; } @@ -205,7 +208,7 @@ void apply_control_profile(game::context& ctx, const json& profile) // Parse key name if (!mapping_element->contains("key")) { - ctx.logger->warning("Control \"" + control_name + "\" has invalid keyboard mapping"); + debug::log::warning("Control \"" + control_name + "\" has invalid keyboard mapping"); continue; } std::string key = (*mapping_element)["key"].get(); @@ -214,14 +217,14 @@ void apply_control_profile(game::context& ctx, const json& profile) input::scancode scancode = keyboard->get_scancode_from_name(key.c_str()); if (scancode == input::scancode::unknown) { - ctx.logger->warning("Control \"" + control_name + "\" mapped to unknown keyboard key \"" + key + "\""); + debug::log::warning("Control \"" + control_name + "\" mapped to unknown keyboard key \"" + key + "\""); continue; } // Map control to keyboard key ctx.input_event_router->add_mapping(input::key_mapping(control, keyboard, scancode)); - ctx.logger->log("Mapped control \"" + control_name + "\" to keyboard key \"" + key + "\""); + debug::log::info("Mapped control \"" + control_name + "\" to keyboard key \"" + key + "\""); } else if (device == "mouse") { @@ -233,58 +236,58 @@ void apply_control_profile(game::context& ctx, const json& profile) // Map control to mouse button ctx.input_event_router->add_mapping(input::mouse_button_mapping(control, mouse, button)); - ctx.logger->log("Mapped control \"" + control_name + "\" to mouse button " + std::to_string(button)); + debug::log::info("Mapped control \"" + control_name + "\" to mouse button " + std::to_string(button)); } else if (mapping_element->contains("wheel")) { // Parse mouse wheel axis std::string wheel = (*mapping_element)["wheel"].get(); - input::mouse_wheel_axis axis; + input::mouse_axis axis; if (wheel == "x+") - axis = input::mouse_wheel_axis::positive_x; + axis = input::mouse_axis::positive_x; else if (wheel == "x-") - axis = input::mouse_wheel_axis::negative_x; + axis = input::mouse_axis::negative_x; else if (wheel == "y+") - axis = input::mouse_wheel_axis::positive_y; + axis = input::mouse_axis::positive_y; else if (wheel == "y-") - axis = input::mouse_wheel_axis::negative_y; + axis = input::mouse_axis::negative_y; else { - ctx.logger->warning("Control \"" + control_name + "\" is mapped to invalid mouse wheel axis \"" + wheel + "\""); + debug::log::warning("Control \"" + control_name + "\" is mapped to invalid mouse wheel axis \"" + wheel + "\""); continue; } // Map control to mouse wheel axis ctx.input_event_router->add_mapping(input::mouse_wheel_mapping(control, mouse, axis)); - ctx.logger->log("Mapped control \"" + control_name + "\" to mouse wheel axis " + wheel); + debug::log::info("Mapped control \"" + control_name + "\" to mouse wheel axis " + wheel); } else if (mapping_element->contains("motion")) { std::string motion = (*mapping_element)["motion"].get(); - input::mouse_motion_axis axis; + input::mouse_axis axis; if (motion == "x+") - axis = input::mouse_motion_axis::positive_x; + axis = input::mouse_axis::positive_x; else if (motion == "x-") - axis = input::mouse_motion_axis::negative_x; + axis = input::mouse_axis::negative_x; else if (motion == "y+") - axis = input::mouse_motion_axis::positive_y; + axis = input::mouse_axis::positive_y; else if (motion == "y-") - axis = input::mouse_motion_axis::negative_y; + axis = input::mouse_axis::negative_y; else { - ctx.logger->warning("Control \"" + control_name + "\" is mapped to invalid mouse motion axis \"" + motion + "\""); + debug::log::warning("Control \"" + control_name + "\" is mapped to invalid mouse motion axis \"" + motion + "\""); continue; } // Map control to mouse motion axis ctx.input_event_router->add_mapping(input::mouse_motion_mapping(control, mouse, axis)); - ctx.logger->log("Mapped control \"" + control_name + "\" to mouse motion axis " + motion); + debug::log::info("Mapped control \"" + control_name + "\" to mouse motion axis " + motion); } else { - ctx.logger->warning("Control \"" + control_name + "\" has invalid mouse mapping"); + debug::log::warning("Control \"" + control_name + "\" has invalid mouse mapping"); continue; } } @@ -298,14 +301,14 @@ void apply_control_profile(game::context& ctx, const json& profile) auto button_it = gamepad_button_map.find(button); if (button_it == gamepad_button_map.end()) { - ctx.logger->warning("Control \"" + control_name + "\" is mapped to invalid gamepad button \"" + button + "\""); + debug::log::warning("Control \"" + control_name + "\" is mapped to invalid gamepad button \"" + button + "\""); continue; } // Map control to gamepad button ctx.input_event_router->add_mapping(input::gamepad_button_mapping(control, gamepad, button_it->second)); - ctx.logger->log("Mapped control \"" + control_name + "\" to gamepad button " + button); + debug::log::info("Mapped control \"" + control_name + "\" to gamepad button " + button); } else if (mapping_element->contains("axis")) { @@ -316,7 +319,7 @@ void apply_control_profile(game::context& ctx, const json& profile) auto axis_it = gamepad_axis_map.find(axis_name); if (axis_it == gamepad_axis_map.end()) { - ctx.logger->warning("Control \"" + control_name + "\" is mapped to invalid gamepad axis \"" + axis_name + "\""); + debug::log::warning("Control \"" + control_name + "\" is mapped to invalid gamepad axis \"" + axis_name + "\""); continue; } @@ -324,7 +327,7 @@ void apply_control_profile(game::context& ctx, const json& profile) const char axis_sign = axis.back(); if (axis_sign != '-' && axis_sign != '+') { - ctx.logger->warning("Control \"" + control_name + "\" is mapped to gamepad axis with invalid sign \"" + axis_sign + "\""); + debug::log::warning("Control \"" + control_name + "\" is mapped to gamepad axis with invalid sign \"" + axis_sign + "\""); continue; } bool axis_negative = (axis_sign == '-'); @@ -332,30 +335,32 @@ void apply_control_profile(game::context& ctx, const json& profile) // Map control to gamepad axis ctx.input_event_router->add_mapping(input::gamepad_axis_mapping(control, gamepad, axis_it->second, axis_negative)); - ctx.logger->log("Mapped control \"" + control_name + "\" to gamepad axis " + axis); + debug::log::info("Mapped control \"" + control_name + "\" to gamepad axis " + axis); } else { - ctx.logger->log("Control \"" + control_name + "\" has invalid gamepad mapping"); + debug::log::info("Control \"" + control_name + "\" has invalid gamepad mapping"); continue; } } else { - ctx.logger->warning("Control \"" + control_name + "\" bound to unknown device \"" + device + "\""); + debug::log::warning("Control \"" + control_name + "\" bound to unknown device \"" + device + "\""); } } } } + */ } void save_control_profile(game::context& ctx) { + /* std::filesystem::path path; if (ctx.config->contains("control_profile")) path = ctx.config_path / "controls" / (*ctx.config)["control_profile"].get(); - ctx.logger->push_task("Saving control profile to \"" + path.string() + "\""); + debug::log::trace("Saving control profile to \"{}\"...", path.string()); try { json control_profile; @@ -402,16 +407,16 @@ void save_control_profile(game::context& ctx) mapping_element["device"] = "mouse"; switch (wheel_mapping->axis) { - case input::mouse_wheel_axis::negative_x: + case input::mouse_axis::negative_x: mapping_element["wheel"] = "x-"; break; - case input::mouse_wheel_axis::positive_x: + case input::mouse_axis::positive_x: mapping_element["wheel"] = "x+"; break; - case input::mouse_wheel_axis::negative_y: + case input::mouse_axis::negative_y: mapping_element["wheel"] = "y-"; break; - case input::mouse_wheel_axis::positive_y: + case input::mouse_axis::positive_y: mapping_element["wheel"] = "y+"; break; default: @@ -427,16 +432,16 @@ void save_control_profile(game::context& ctx) mapping_element["device"] = "mouse"; switch (motion_mapping->axis) { - case input::mouse_motion_axis::negative_x: + case input::mouse_axis::negative_x: mapping_element["motion"] = "x-"; break; - case input::mouse_motion_axis::positive_x: + case input::mouse_axis::positive_x: mapping_element["motion"] = "x+"; break; - case input::mouse_motion_axis::negative_y: + case input::mouse_axis::negative_y: mapping_element["motion"] = "y-"; break; - case input::mouse_motion_axis::positive_y: + case input::mouse_axis::positive_y: mapping_element["motion"] = "y+"; break; default: @@ -566,13 +571,15 @@ void save_control_profile(game::context& ctx) } catch (...) { - ctx.logger->pop_task(EXIT_FAILURE); + debug::log::pop_task(EXIT_FAILURE); } - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); + */ } void apply_gamepad_calibration(input::gamepad& gamepad, const json& calibration) { + /* // Parse and apply activation thresholds if (calibration.contains("leftx_activation")) { @@ -661,6 +668,7 @@ void apply_gamepad_calibration(input::gamepad& gamepad, const json& calibration) auto curve = parse_response_curve(calibration["righttrigger_response_curve"].get()); gamepad.set_response_curve(input::gamepad_axis::right_trigger, curve); } + */ } } // namespace game diff --git a/src/game/fonts.cpp b/src/game/fonts.cpp index 7d47028..f20229e 100644 --- a/src/game/fonts.cpp +++ b/src/game/fonts.cpp @@ -74,7 +74,7 @@ void load_fonts(game::context& ctx) { if (auto it = ctx.strings->find("font_dyslexia"); it != ctx.strings->end() && !it->second.empty() && it->second[0] != '#') { - ctx.logger->log(it->second); + debug::log::info(it->second); ctx.typefaces["dyslexia"] = ctx.resource_manager->load(it->second); dyslexia_font_loaded = true; } @@ -145,7 +145,7 @@ void load_fonts(game::context& ctx) const float menu_font_size_px = (menu_font_size_pt * dpi) / 72.0f; const float title_font_size_px = (title_font_size_pt * dpi) / 72.0f; - ctx.logger->log("font size: " + std::to_string(menu_font_size_px)); + debug::log::info("font size: " + std::to_string(menu_font_size_px)); // Build debug font if (auto it = ctx.typefaces.find("monospace"); it != ctx.typefaces.end()) diff --git a/src/game/graphics.cpp b/src/game/graphics.cpp index 2b57774..4dc84bc 100644 --- a/src/game/graphics.cpp +++ b/src/game/graphics.cpp @@ -18,20 +18,22 @@ */ #include "game/graphics.hpp" -#include "render/passes/bloom-pass.hpp" -#include "render/passes/fxaa-pass.hpp" -#include "render/passes/final-pass.hpp" -#include "render/passes/resample-pass.hpp" +#include "config.hpp" +#include "debug/log.hpp" #include "gl/framebuffer.hpp" #include "gl/texture-2d.hpp" -#include "gl/texture-wrapping.hpp" #include "gl/texture-filter.hpp" -#include "debug/logger.hpp" -#include "utility/timestamp.hpp" +#include "gl/texture-wrapping.hpp" +#include "render/passes/bloom-pass.hpp" +#include "render/passes/final-pass.hpp" +#include "render/passes/fxaa-pass.hpp" +#include "render/passes/resample-pass.hpp" +#include +#include +#include #include #include #include -#include namespace game { namespace graphics { @@ -40,7 +42,7 @@ static void reroute_framebuffers(game::context& ctx); void create_framebuffers(game::context& ctx) { - ctx.logger->push_task("Creating framebuffers"); + debug::log::trace("Creating framebuffers..."); // Load render resolution scale from config ctx.render_scale = 1.0f; @@ -93,12 +95,12 @@ void create_framebuffers(game::context& ctx) ctx.shadow_map_framebuffer = new gl::framebuffer(shadow_map_resolution, shadow_map_resolution); ctx.shadow_map_framebuffer->attach(gl::framebuffer_attachment_type::depth, ctx.shadow_map_depth_texture); - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::trace("Created framebuffers"); } void destroy_framebuffers(game::context& ctx) { - ctx.logger->push_task("Destroying framebuffers"); + debug::log::trace("Destroying framebuffers..."); // Delete HDR framebuffer and its attachments delete ctx.hdr_framebuffer; @@ -125,12 +127,12 @@ void destroy_framebuffers(game::context& ctx) delete ctx.shadow_map_depth_texture; ctx.shadow_map_depth_texture = nullptr; - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::trace("Destroyed framebuffers"); } void change_render_resolution(game::context& ctx, float scale) { - ctx.logger->push_task("Changing render resolution"); + debug::log::trace("Changing render resolution to {}...", scale); // Update render resolution scale ctx.render_scale = scale; @@ -164,20 +166,24 @@ void change_render_resolution(game::context& ctx, float scale) } reroute_framebuffers(ctx); - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::trace("Changed render resolution to {}", scale); } void save_screenshot(game::context& ctx) { - // Determine screenshot path - std::string filename = "antkeeper-" + timestamp() + ".png"; - std::filesystem::path path = ctx.config_path / "gallery" / filename; + // Determine timestamped screenshot filename + const auto time = std::chrono::floor(std::chrono::system_clock::now()); + const std::string screenshot_filename = std::format("{0}-{1:%Y%m%d}T{1:%H%M%S}Z.png", config::application_name, time); - ctx.logger->push_task("Saving screenshot to \"" + path.string() + "\""); + // Determine path to screenshot file + std::filesystem::path screenshot_filepath = ctx.config_path / "gallery" / screenshot_filename; + std::string screenshot_filepath_string = screenshot_filepath.string(); + debug::log::debug("Saving screenshot to \"{}\"...", screenshot_filepath_string); + // Get viewport dimensions const int2& viewport_dimensions = ctx.app->get_viewport_dimensions(); - // Allocate image + // Allocate screenshot image std::shared_ptr frame = std::make_shared(); frame->format(1, 3); frame->resize(viewport_dimensions.x(), viewport_dimensions.y()); @@ -189,14 +195,16 @@ void save_screenshot(game::context& ctx) // Write screenshot file in separate thread std::thread ( - [frame, path] + [frame, path = std::move(screenshot_filepath_string)] { stbi_flip_vertically_on_write(1); - stbi_write_png(path.string().c_str(), frame->get_width(), frame->get_height(), frame->get_channel_count(), frame->data(), frame->get_width() * frame->get_channel_count()); + stbi_write_png(path.c_str(), frame->get_width(), frame->get_height(), frame->get_channel_count(), frame->data(), frame->get_width() * frame->get_channel_count()); + + debug::log::debug("Saved screenshot to \"{}\"", path); } ).detach(); - ctx.logger->pop_task(EXIT_SUCCESS); + } void toggle_bloom(game::context& ctx, bool enabled) diff --git a/src/game/load.cpp b/src/game/load.cpp index ce839fb..0ec7abe 100644 --- a/src/game/load.cpp +++ b/src/game/load.cpp @@ -18,44 +18,28 @@ */ #include "game/load.hpp" -#include "game/world.hpp" -#include "application.hpp" -#include "debug/logger.hpp" +#include "debug/log.hpp" #include "resources/json.hpp" #include "resources/resource-manager.hpp" -#include "render/model.hpp" -#include "render/material.hpp" -#include "render/passes/sky-pass.hpp" -#include "render/passes/ground-pass.hpp" -#include "game/system/astronomy.hpp" -#include "game/system/terrain.hpp" -#include "math/noise/noise.hpp" -#include "math/hash/hash.hpp" -#include -#include -#include -#include "resources/image.hpp" - -#include -#include - namespace game { namespace load { void colony(game::context& ctx, const std::filesystem::path& path) { - ctx.logger->push_task("Loading colony from \"" + path.string() + "\""); + const std::string path_string = path.string(); + + debug::log::trace("Loading colony from \"{}\"...", path_string); try { json* data = ctx.resource_manager->load(path); + debug::log::trace("Loaded colony from \"{}\"", path_string); } catch (...) { - ctx.logger->pop_task(EXIT_FAILURE); + debug::log::error("Failed to load colony from \"{}\"", path_string); } - ctx.logger->pop_task(EXIT_SUCCESS); } } // namespace load diff --git a/src/game/menu.cpp b/src/game/menu.cpp index 7712710..70fa0f0 100644 --- a/src/game/menu.cpp +++ b/src/game/menu.cpp @@ -295,7 +295,8 @@ void fade_out_bg(game::context& ctx) } void setup_controls(game::context& ctx) -{ +{ + /* ctx.controls["menu_up"]->set_activated_callback ( [&ctx]() @@ -459,10 +460,12 @@ void setup_controls(game::context& ctx) } } ); + */ } void clear_controls(game::context& ctx) { + /* ctx.controls["menu_up"]->set_activated_callback(nullptr); ctx.controls["menu_down"]->set_activated_callback(nullptr); ctx.controls["menu_left"]->set_activated_callback(nullptr); @@ -472,6 +475,7 @@ void clear_controls(game::context& ctx) ctx.menu_mouse_tracker->set_mouse_moved_callback(nullptr); ctx.menu_mouse_tracker->set_mouse_button_pressed_callback(nullptr); + */ } } // namespace menu diff --git a/src/game/save.cpp b/src/game/save.cpp index 27be1f9..e3bd578 100644 --- a/src/game/save.cpp +++ b/src/game/save.cpp @@ -19,7 +19,7 @@ #include "game/save.hpp" #include "application.hpp" -#include "debug/logger.hpp" +#include "debug/log.hpp" #include "resources/json.hpp" #include @@ -29,7 +29,9 @@ namespace save { void colony(game::context& ctx) { std::filesystem::path path = ctx.saves_path / "colony.sav"; - ctx.logger->push_task("Saving colony to \"" + path.string() + "\""); + const std::string path_string = path.string(); + + debug::log::trace("Saving colony to \"{}\"...", path_string); try { // Construct JSON data describing the colony @@ -67,28 +69,32 @@ void colony(game::context& ctx) std::ofstream file(path); file << data; + + debug::log::trace("Saved colony to \"{}\"", path_string); } catch (...) { - ctx.logger->pop_task(EXIT_FAILURE); + debug::log::error("Failed to save colony to \"{}\"", path_string); } - ctx.logger->pop_task(EXIT_SUCCESS); } void config(game::context& ctx) { std::filesystem::path path = ctx.config_path / "config.json"; - ctx.logger->push_task("Saving config to \"" + path.string() + "\""); + const std::string path_string = path.string(); + + debug::log::trace("Saving config to \"{}\"...", path_string); try { std::ofstream file(path); file << *(ctx.config); + + debug::log::trace("Saved config to \"{}\"", path_string); } catch (...) { - ctx.logger->pop_task(EXIT_FAILURE); + debug::log::error("Failed to save config to \"{}\"", path_string); } - ctx.logger->pop_task(EXIT_SUCCESS); } } // namespace save diff --git a/src/game/state/boot.cpp b/src/game/state/boot.cpp index 264caad..984a7b6 100644 --- a/src/game/state/boot.cpp +++ b/src/game/state/boot.cpp @@ -17,17 +17,41 @@ * along with Antkeeper source code. If not, see . */ -#include "game/state/boot.hpp" #include "animation/animation.hpp" #include "animation/animator.hpp" #include "animation/ease.hpp" #include "animation/screen-transition.hpp" #include "animation/timeline.hpp" #include "application.hpp" +#include "color/color.hpp" +#include "config.hpp" #include "debug/cli.hpp" -#include "debug/console-commands.hpp" -#include "debug/logger.hpp" +#include "debug/log.hpp" +#include "entity/commands.hpp" #include "game/context.hpp" +#include "game/controls.hpp" +#include "game/fonts.hpp" +#include "game/graphics.hpp" +#include "game/menu.hpp" +#include "game/save.hpp" +#include "game/state/boot.hpp" +#include "game/state/splash.hpp" +#include "game/system/astronomy.hpp" +#include "game/system/atmosphere.hpp" +#include "game/system/behavior.hpp" +#include "game/system/blackbody.hpp" +#include "game/system/camera.hpp" +#include "game/system/collision.hpp" +#include "game/system/constraint.hpp" +#include "game/system/locomotion.hpp" +#include "game/system/orbit.hpp" +#include "game/system/render.hpp" +#include "game/system/spatial.hpp" +#include "game/system/spring.hpp" +#include "game/system/steering.hpp" +#include "game/system/subterrain.hpp" +#include "game/system/terrain.hpp" +#include "game/system/vegetation.hpp" #include "gl/framebuffer.hpp" #include "gl/pixel-format.hpp" #include "gl/pixel-type.hpp" @@ -38,67 +62,38 @@ #include "gl/vertex-array.hpp" #include "gl/vertex-attribute.hpp" #include "gl/vertex-buffer.hpp" +#include "input/gamepad.hpp" +#include "input/keyboard.hpp" +#include "input/mapper.hpp" +#include "input/mouse.hpp" +#include "input/scancode.hpp" +#include "render/compositor.hpp" #include "render/material-flags.hpp" #include "render/material-property.hpp" #include "render/passes/bloom-pass.hpp" #include "render/passes/clear-pass.hpp" #include "render/passes/final-pass.hpp" #include "render/passes/fxaa-pass.hpp" -#include "render/passes/resample-pass.hpp" +#include "render/passes/ground-pass.hpp" #include "render/passes/material-pass.hpp" #include "render/passes/outline-pass.hpp" +#include "render/passes/resample-pass.hpp" #include "render/passes/shadow-map-pass.hpp" #include "render/passes/sky-pass.hpp" -#include "render/passes/ground-pass.hpp" -#include "render/vertex-attribute.hpp" -#include "render/compositor.hpp" #include "render/renderer.hpp" -#include "resources/resource-manager.hpp" +#include "render/vertex-attribute.hpp" #include "resources/file-buffer.hpp" +#include "resources/resource-manager.hpp" #include "scene/scene.hpp" -#include "game/state/splash.hpp" -#include "game/system/behavior.hpp" -#include "game/system/camera.hpp" -#include "game/system/collision.hpp" -#include "game/system/constraint.hpp" -#include "game/system/locomotion.hpp" -#include "game/system/render.hpp" -#include "game/system/subterrain.hpp" -#include "game/system/terrain.hpp" -#include "game/system/vegetation.hpp" -#include "game/system/spatial.hpp" -#include "game/system/astronomy.hpp" -#include "game/system/blackbody.hpp" -#include "game/system/atmosphere.hpp" -#include "game/system/orbit.hpp" -#include "game/system/steering.hpp" -#include "game/system/spring.hpp" -#include "entity/commands.hpp" #include "utility/paths.hpp" -#include "event/event-dispatcher.hpp" -#include "input/event-router.hpp" -#include "input/mapper.hpp" -#include "input/listener.hpp" -#include "input/gamepad.hpp" -#include "input/mouse.hpp" -#include "input/keyboard.hpp" -#include "config.hpp" -#include "input/scancode.hpp" -#include "game/fonts.hpp" -#include "game/controls.hpp" -#include "game/save.hpp" -#include "game/menu.hpp" -#include "game/graphics.hpp" -#include "utility/timestamp.hpp" -#include "color/color.hpp" +#include #include #include +#include #include #include #include #include -#include -#include namespace game { namespace state { @@ -106,15 +101,12 @@ namespace state { boot::boot(game::context& ctx, int argc, char** argv): game::state::base(ctx) { - // Allocate application logger - ctx.logger = new debug::logger(); - // Boot process - ctx.logger->push_task("Booting up"); + debug::log::trace("Booting up..."); try { // Allocate application - ctx.app = new application(*ctx.logger); + ctx.app = new application(); // Parse command line options parse_options(argc, argv); @@ -137,22 +129,23 @@ boot::boot(game::context& ctx, int argc, char** argv): } catch (const std::exception& e) { - ctx.logger->error("Caught exception: \"" + std::string(e.what()) + "\""); - ctx.logger->pop_task(EXIT_FAILURE); - return; + debug::log::fatal("Boot up failed: unhandled exception: {}", e.what()); + throw e; } - ctx.logger->pop_task(EXIT_SUCCESS); + + debug::log::trace("Boot up complete"); // Push splash state ctx.state_machine.emplace(new game::state::splash(ctx)); // Enter main loop + debug::log::trace("Entered main loop"); loop(); } boot::~boot() { - ctx.logger->push_task("Booting down"); + debug::log::trace("Booting down..."); try { @@ -164,18 +157,16 @@ boot::~boot() } catch (const std::exception& e) { - ctx.logger->error("Caught exception: \"" + std::string(e.what()) + "\""); - ctx.logger->pop_task(EXIT_FAILURE); - return; + debug::log::fatal("Boot down failed: unhandled exception: {}", e.what()); + throw e; } - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::trace("Boot down complete"); } void boot::parse_options(int argc, char** argv) { - debug::logger* logger = ctx.logger; - logger->push_task("Parsing command line options"); + debug::log::trace("Parsing {} command line arguments...", argc); try { @@ -221,44 +212,32 @@ void boot::parse_options(int argc, char** argv) // --windowed if (result.count("windowed")) - option_windowed = true; + option_windowed = true; + + debug::log::trace("Parsed {} command line arguments", argc); } catch (const std::exception& e) { - logger->error("Exception caught: \"" + std::string(e.what()) + "\""); - logger->pop_task(EXIT_FAILURE); - return; + debug::log::warning("Exception caught while parsing command line arguments: {}", e.what()); } - - logger->pop_task(EXIT_SUCCESS); } void boot::setup_resources() { - debug::logger* logger = ctx.logger; - // Setup resource manager - ctx.resource_manager = new resource_manager(logger); - - // Determine application name - std::string application_name; - #if defined(_WIN32) || defined(__APPLE__) - application_name = "Antkeeper"; - #else - application_name = "antkeeper"; - #endif + ctx.resource_manager = new resource_manager(); // Detect paths - ctx.data_path = get_data_path(application_name); - ctx.config_path = get_config_path(application_name); + ctx.data_path = get_data_path(config::application_name); + ctx.config_path = get_config_path(config::application_name); ctx.mods_path = ctx.config_path / "mods"; ctx.saves_path = ctx.config_path / "saves"; ctx.screenshots_path = ctx.config_path / "gallery"; ctx.controls_path = ctx.config_path / "controls"; // Log resource paths - logger->log("Detected data path as \"" + ctx.data_path.string()); - logger->log("Detected config path as \"" + ctx.config_path.string()); + debug::log::info("Data path: \"{}\"", ctx.data_path.string()); + debug::log::info("Config path: \"{}\"", ctx.config_path.string()); // Create nonexistent config directories std::vector config_paths; @@ -271,26 +250,20 @@ void boot::setup_resources() { if (!std::filesystem::exists(path)) { - logger->push_task("Creating directory \"" + path.string()); + const std::string path_string = path.string(); + + debug::log::trace("Creating directory \"{}\"...", path_string); if (std::filesystem::create_directories(path)) { - logger->pop_task(EXIT_SUCCESS); + debug::log::trace("Created directory \"{}\"", path_string); } else { - logger->pop_task(EXIT_FAILURE); + debug::log::error("Failed to create directory \"{}\"", path_string); } } } - // Redirect logger output to log file on non-debug builds - #if defined(NDEBUG) - std::filesystem::path log_path = ctx.config_path / "log.txt"; - ctx.log_filestream.open(log_path); - ctx.log_filestream << logger->get_history(); - logger->redirect(&ctx.log_filestream); - #endif - // Scan for mods std::vector mod_paths; for (const auto& directory_entry: std::filesystem::directory_iterator(ctx.mods_path)) @@ -328,24 +301,23 @@ void boot::setup_resources() void boot::load_config() { - debug::logger* logger = ctx.logger; - logger->push_task("Loading config"); + debug::log::trace("Loading config..."); // Load config file ctx.config = ctx.resource_manager->load("config.json"); - if (!ctx.config) + if (ctx.config) { - logger->pop_task(EXIT_FAILURE); - return; + debug::log::trace("Loaded config"); + } + else + { + debug::log::error("Failed to load config"); } - - logger->pop_task(EXIT_SUCCESS); } void boot::load_strings() { - debug::logger* logger = ctx.logger; - logger->push_task("Loading strings"); + debug::log::trace("Loading strings..."); ctx.string_table = ctx.resource_manager->load("strings.csv"); @@ -360,19 +332,18 @@ void boot::load_strings() } ctx.language_count = (*ctx.string_table)[0].size() - 2; - logger->log("language count: " + std::to_string(ctx.language_count)); - logger->log("language index: " + std::to_string(ctx.language_index)); - logger->log("language code: " + ctx.language_code); + debug::log::info("Languages available: {}", ctx.language_count); + debug::log::info("Language index: {}", ctx.language_index); + debug::log::info("Language code: {}", ctx.language_code); ctx.strings = &ctx.string_table_map[ctx.language_code]; - logger->pop_task(EXIT_SUCCESS); + debug::log::trace("Loaded strings"); } void boot::setup_window() { - debug::logger* logger = ctx.logger; - logger->push_task("Setting up window"); + debug::log::trace("Setting up window..."); application* app = ctx.app; json* config = ctx.config; @@ -425,13 +396,12 @@ void boot::setup_window() app->show_window(); ctx.app->swap_buffers(); - logger->pop_task(EXIT_SUCCESS); + debug::log::trace("Set up window"); } void boot::setup_rendering() { - debug::logger* logger = ctx.logger; - logger->push_task("Setting up rendering"); + debug::log::trace("Setting up rendering..."); // Get rasterizer from application ctx.rasterizer = ctx.app->get_rasterizer(); @@ -523,7 +493,6 @@ void boot::setup_rendering() ctx.underground_material_pass = new render::material_pass(ctx.rasterizer, ctx.hdr_framebuffer, ctx.resource_manager); ctx.underground_material_pass->set_fallback_material(ctx.fallback_material); - ctx.app->get_event_dispatcher()->subscribe(ctx.underground_material_pass); ctx.underground_compositor = new render::compositor(); ctx.underground_compositor->add_pass(ctx.underground_clear_pass); @@ -549,14 +518,12 @@ void boot::setup_rendering() ctx.sky_pass = new render::sky_pass(ctx.rasterizer, ctx.hdr_framebuffer, ctx.resource_manager); ctx.sky_pass->set_enabled(false); ctx.sky_pass->set_magnification(3.0f); - ctx.app->get_event_dispatcher()->subscribe(ctx.sky_pass); ctx.ground_pass = new render::ground_pass(ctx.rasterizer, ctx.hdr_framebuffer, ctx.resource_manager); ctx.ground_pass->set_enabled(false); ctx.surface_material_pass = new render::material_pass(ctx.rasterizer, ctx.hdr_framebuffer, ctx.resource_manager); ctx.surface_material_pass->set_fallback_material(ctx.fallback_material); - ctx.app->get_event_dispatcher()->subscribe(ctx.surface_material_pass); ctx.surface_outline_pass = new render::outline_pass(ctx.rasterizer, ctx.hdr_framebuffer, ctx.resource_manager); ctx.surface_outline_pass->set_outline_width(0.25f); @@ -634,13 +601,12 @@ void boot::setup_rendering() ctx.renderer = new render::renderer(); ctx.renderer->set_billboard_vao(ctx.billboard_vao); - logger->pop_task(EXIT_SUCCESS); + debug::log::trace("Set up rendering"); } void boot::setup_audio() { - debug::logger* logger = ctx.logger; - logger->push_task("Setting up audio"); + debug::log::trace("Setting up audio..."); // Load master volume config ctx.master_volume = 1.0f; @@ -673,11 +639,11 @@ void boot::setup_audio() ctx.captions_size = (*ctx.config)["captions_size"].get(); // Open audio device - logger->push_task("Opening audio device"); + debug::log::trace("Opening audio device..."); ctx.alc_device = alcOpenDevice(nullptr); if (!ctx.alc_device) { - logger->pop_task(EXIT_FAILURE); + debug::log::error("Failed to open audio device: AL error code {}", alGetError()); return; } else @@ -689,30 +655,29 @@ void boot::setup_audio() if (alcGetError(ctx.alc_device) != AL_NO_ERROR || !alc_device_name) alc_device_name = alcGetString(ctx.alc_device, ALC_DEVICE_SPECIFIER); - logger->log("Opened audio device \"" + std::string(alc_device_name) + "\""); - - logger->pop_task(EXIT_SUCCESS); + // Log audio device name + debug::log::info("Opened audio device \"{}\"", alc_device_name); } // Create audio context - logger->push_task("Creating audio context"); + debug::log::trace("Creating audio context..."); ctx.alc_context = alcCreateContext(ctx.alc_device, nullptr); if (!ctx.alc_context) { - logger->pop_task(EXIT_FAILURE); + debug::log::error("Failed to create audio context: ALC error code {}", alcGetError(ctx.alc_device)); alcCloseDevice(ctx.alc_device); return; } else { - logger->pop_task(EXIT_SUCCESS); + debug::log::trace("Created audio context"); } // Make audio context current - logger->push_task("Making audio context current"); + debug::log::trace("Making audio context current..."); if (alcMakeContextCurrent(ctx.alc_context) == ALC_FALSE) { - logger->pop_task(EXIT_FAILURE); + debug::log::error("Failed to make audio context current: ALC error code {}", alcGetError(ctx.alc_device)); if (ctx.alc_context != nullptr) { alcDestroyContext(ctx.alc_context); @@ -722,16 +687,15 @@ void boot::setup_audio() } else { - logger->pop_task(EXIT_SUCCESS); + debug::log::trace("Made audio context current"); } - logger->pop_task(EXIT_SUCCESS); + debug::log::trace("Set up audio"); } void boot::setup_scenes() { - debug::logger* logger = ctx.logger; - logger->push_task("Setting up scenes"); + debug::log::trace("Setting up scenes..."); // Get default framebuffer const auto& viewport_dimensions = ctx.rasterizer->get_default_framebuffer().get_dimensions(); @@ -768,7 +732,6 @@ void boot::setup_scenes() // Setup UI scene { ctx.ui_scene = new scene::collection(); - // Menu BG billboard render::material* menu_bg_material = new render::material(); @@ -785,7 +748,6 @@ void boot::setup_scenes() ctx.menu_bg_billboard->update_tweens(); // Create camera flash billboard - render::material* flash_material = new render::material(); flash_material->set_shader_program(ctx.resource_manager->load("ui-element-untextured.glsl")); auto flash_tint = flash_material->add_property("tint"); @@ -833,7 +795,7 @@ void boot::setup_scenes() // Clear active scene ctx.active_scene = nullptr; - logger->pop_task(EXIT_SUCCESS); + debug::log::trace("Set up scenes"); } void boot::setup_animation() @@ -935,8 +897,6 @@ void boot::setup_entities() void boot::setup_systems() { - event_dispatcher* event_dispatcher = ctx.app->get_event_dispatcher(); - const auto& viewport_dimensions = ctx.app->get_viewport_dimensions(); float4 viewport = {0.0f, 0.0f, static_cast(viewport_dimensions[0]), static_cast(viewport_dimensions[1])}; @@ -957,7 +917,6 @@ void boot::setup_systems() // Setup camera system ctx.camera_system = new game::system::camera(*ctx.entity_registry); ctx.camera_system->set_viewport(viewport); - event_dispatcher->subscribe(ctx.camera_system); // Setup subterrain system ctx.subterrain_system = new game::system::subterrain(*ctx.entity_registry, ctx.resource_manager); @@ -1014,36 +973,23 @@ void boot::setup_systems() void boot::setup_controls() { - event_dispatcher* event_dispatcher = ctx.app->get_event_dispatcher(); - - // Setup input event routing - ctx.input_event_router = new input::event_router(); - ctx.input_event_router->set_event_dispatcher(event_dispatcher); - - // Setup input mapper - ctx.input_mapper = new input::mapper(); - ctx.input_mapper->set_event_dispatcher(event_dispatcher); - - // Setup input listener - ctx.input_listener = new input::listener(); - ctx.input_listener->set_event_dispatcher(event_dispatcher); - // Load SDL game controller mappings database - ctx.logger->push_task("Loading SDL game controller mappings from database"); + debug::log::trace("Loading SDL game controller mappings..."); file_buffer* game_controller_db = ctx.resource_manager->load("gamecontrollerdb.txt"); if (!game_controller_db) { - ctx.logger->pop_task(EXIT_FAILURE); + debug::log::error("Failed to load SDL game controller mappings"); } else { ctx.app->add_game_controller_mappings(game_controller_db->data(), game_controller_db->size()); + debug::log::trace("Loaded SDL game controller mappings"); + ctx.resource_manager->unload("gamecontrollerdb.txt"); - ctx.logger->pop_task(EXIT_SUCCESS); } // Load controls - ctx.logger->push_task("Loading controls"); + debug::log::trace("Loading controls..."); try { // If a control profile is set in the config file @@ -1060,69 +1006,92 @@ void boot::setup_controls() } // Calibrate gamepads - for (input::gamepad* gamepad: ctx.app->get_gamepads()) - { - ctx.logger->push_task("Loading calibration for gamepad " + gamepad->get_guid()); - json* calibration = game::load_gamepad_calibration(ctx, *gamepad); - if (!calibration) - { - ctx.logger->pop_task(EXIT_FAILURE); + // for (input::gamepad* gamepad: ctx.app->get_gamepads()) + // { + // const std::string uuid_string = gamepad->get_uuid().to_string(); + + // debug::log::push_task("Loading calibration for gamepad " + uuid_string); + // json* calibration = game::load_gamepad_calibration(ctx, *gamepad); + // if (!calibration) + // { + // debug::log::pop_task(EXIT_FAILURE); - ctx.logger->push_task("Generating default calibration for gamepad " + gamepad->get_guid()); - json default_calibration = game::default_gamepad_calibration(); - apply_gamepad_calibration(*gamepad, default_calibration); + // debug::log::push_task("Generating default calibration for gamepad " + uuid_string); + // json default_calibration = game::default_gamepad_calibration(); + // apply_gamepad_calibration(*gamepad, default_calibration); - if (!save_gamepad_calibration(ctx, *gamepad, default_calibration)) - ctx.logger->pop_task(EXIT_FAILURE); - else - ctx.logger->pop_task(EXIT_SUCCESS); - } - else - { - ctx.logger->pop_task(EXIT_SUCCESS); - apply_gamepad_calibration(*gamepad, *calibration); - } - } + // if (!save_gamepad_calibration(ctx, *gamepad, default_calibration)) + // debug::log::pop_task(EXIT_FAILURE); + // else + // debug::log::pop_task(EXIT_SUCCESS); + // } + // else + // { + // debug::log::pop_task(EXIT_SUCCESS); + // apply_gamepad_calibration(*gamepad, *calibration); + // } + // } - // Toggle fullscreen - ctx.controls["toggle_fullscreen"]->set_activated_callback + // Setup fullscreen control + ctx.control_subscriptions.emplace_front ( - [&ctx = this->ctx]() - { - bool fullscreen = !ctx.app->is_fullscreen(); - - ctx.app->set_fullscreen(fullscreen); - - if (!fullscreen) + ctx.fullscreen_control.get_activated_channel().subscribe + ( + [&ctx = this->ctx](const auto& event) { - int2 resolution; - resolution.x() = (*ctx.config)["windowed_resolution"][0].get(); - resolution.y() = (*ctx.config)["windowed_resolution"][1].get(); + bool fullscreen = !ctx.app->is_fullscreen(); + + ctx.app->set_fullscreen(fullscreen); - ctx.app->resize_window(resolution.x(), resolution.y()); + if (!fullscreen) + { + int2 resolution; + resolution.x() = (*ctx.config)["windowed_resolution"][0].get(); + resolution.y() = (*ctx.config)["windowed_resolution"][1].get(); + + ctx.app->resize_window(resolution.x(), resolution.y()); + } + + // Save display mode config + (*ctx.config)["fullscreen"] = fullscreen; + game::save::config(ctx); } - - // Save display mode config - (*ctx.config)["fullscreen"] = fullscreen; - game::save::config(ctx); - } + ) + ); + + // Setup screenshot control + ctx.control_subscriptions.emplace_front + ( + ctx.screenshot_control.get_activated_channel().subscribe + ( + [&ctx = this->ctx](const auto& event) + { + game::graphics::save_screenshot(ctx); + } + ) ); - // Screenshot - ctx.controls["screenshot"]->set_activated_callback(std::bind(game::graphics::save_screenshot, std::ref(ctx))); + // Map and enable window controls + ctx.window_controls.add_mapping(ctx.fullscreen_control, input::key_mapping(nullptr, input::scancode::f11, false)); + ctx.window_controls.add_mapping(ctx.screenshot_control, input::key_mapping(nullptr, input::scancode::f12, false)); + ctx.window_controls.connect(ctx.app->get_device_manager().get_event_queue()); // Set activation threshold for menu navigation controls to mitigate drifting gamepad axes - const float menu_activation_threshold = 0.1f; - ctx.controls["menu_up"]->set_activation_threshold(menu_activation_threshold); - ctx.controls["menu_down"]->set_activation_threshold(menu_activation_threshold); - ctx.controls["menu_left"]->set_activation_threshold(menu_activation_threshold); - ctx.controls["menu_right"]->set_activation_threshold(menu_activation_threshold); + auto menu_control_threshold = [](float x) -> bool + { + return x > 0.1f; + }; + ctx.menu_up_control.set_threshold_function(menu_control_threshold); + ctx.menu_down_control.set_threshold_function(menu_control_threshold); + ctx.menu_left_control.set_threshold_function(menu_control_threshold); + ctx.menu_right_control.set_threshold_function(menu_control_threshold); + + debug::log::trace("Loaded controls"); } catch (...) { - ctx.logger->pop_task(EXIT_FAILURE); + debug::log::error("Failed to load controls"); } - ctx.logger->pop_task(EXIT_SUCCESS); } void boot::setup_ui() @@ -1138,39 +1107,32 @@ void boot::setup_ui() ctx.dyslexia_font = (*ctx.config)["dyslexia_font"].get(); // Load fonts - ctx.logger->push_task("Loading fonts"); + debug::log::trace("Loading fonts..."); try { game::load_fonts(ctx); + debug::log::trace("Loaded fonts"); } catch (...) { - ctx.logger->pop_task(EXIT_FAILURE); + debug::log::error("Failed to load fonts"); } - ctx.logger->pop_task(EXIT_SUCCESS); // Setup UI resize handler - ctx.ui_resize_connection = ctx.app->get_window_size_signal().connect + ctx.ui_resize_subscription = ctx.app->get_window_resized_channel().subscribe ( - [&](int w, int h) + [&](const auto& event) { - const float clip_left = static_cast(w) * -0.5f; - const float clip_right = static_cast(w) * 0.5f; - const float clip_top = static_cast(h) * -0.5f; - const float clip_bottom = static_cast(h) * 0.5f; + const float clip_left = static_cast(event.viewport_width) * -0.5f; + const float clip_right = static_cast(event.viewport_width) * 0.5f; + const float clip_top = static_cast(event.viewport_height) * -0.5f; + const float clip_bottom = static_cast(event.viewport_height) * 0.5f; const float clip_near = ctx.ui_camera->get_clip_near(); const float clip_far = ctx.ui_camera->get_clip_far(); ctx.ui_camera->set_orthographic(clip_left, clip_right, clip_top, clip_bottom, clip_near, clip_far); } ); - - // Construct mouse tracker - ctx.menu_mouse_tracker = new ui::mouse_tracker(); - ctx.app->get_event_dispatcher()->subscribe(ctx.menu_mouse_tracker); - ctx.app->get_event_dispatcher()->subscribe(ctx.menu_mouse_tracker); - ctx.app->get_event_dispatcher()->subscribe(ctx.menu_mouse_tracker); - ctx.app->get_event_dispatcher()->subscribe(ctx.menu_mouse_tracker); } void boot::setup_debugging() @@ -1179,13 +1141,7 @@ void boot::setup_debugging() ctx.performance_sampler.set_sample_size(15); ctx.cli = new debug::cli(); - ctx.cli->register_command("echo", debug::cc::echo); - ctx.cli->register_command("exit", std::function(std::bind(&debug::cc::exit, &ctx))); - ctx.cli->register_command("scrot", std::function(std::bind(&debug::cc::scrot, &ctx))); - ctx.cli->register_command("cue", std::function(std::bind(&debug::cc::cue, &ctx, std::placeholders::_1, std::placeholders::_2))); - //std::string cmd = "cue 20 exit"; - //logger->log(cmd); - //logger->log(cli.interpret(cmd)); + //debug::log::info(ctx.cli->interpret("echo hi 123")); } void boot::setup_loop() @@ -1209,11 +1165,7 @@ void boot::setup_loop() // Process events ctx.app->process_events(); - ctx.app->get_event_dispatcher()->update(t); - - // Update controls - for (const auto& control: ctx.controls) - control.second->update(); + ctx.app->get_device_manager().get_event_queue().flush(); // Process function queue while (!ctx.function_queue.empty()) @@ -1287,7 +1239,7 @@ void boot::loop() void boot::shutdown_audio() { - ctx.logger->push_task("Shutting down audio"); + debug::log::trace("Shutting down audio..."); if (ctx.alc_context) { @@ -1300,7 +1252,7 @@ void boot::shutdown_audio() alcCloseDevice(ctx.alc_device); } - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::trace("Shut down audio"); } } // namespace state diff --git a/src/game/state/controls-menu.cpp b/src/game/state/controls-menu.cpp index 19be0cc..36397d7 100644 --- a/src/game/state/controls-menu.cpp +++ b/src/game/state/controls-menu.cpp @@ -23,7 +23,7 @@ #include "game/state/options-menu.hpp" #include "application.hpp" #include "scene/text.hpp" -#include "debug/logger.hpp" +#include "debug/log.hpp" #include "game/menu.hpp" namespace game { @@ -32,7 +32,7 @@ namespace state { controls_menu::controls_menu(game::context& ctx): game::state::base(ctx) { - ctx.logger->push_task("Entering controls menu state"); + debug::log::push_task("Entering controls menu state"); // Construct menu item texts scene::text* keyboard_text = new scene::text(); @@ -151,12 +151,12 @@ controls_menu::controls_menu(game::context& ctx): // Fade in menu game::menu::fade_in(ctx, nullptr); - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } controls_menu::~controls_menu() { - ctx.logger->push_task("Exiting options menu state"); + debug::log::push_task("Exiting options menu state"); // Destruct menu game::menu::clear_controls(ctx); @@ -165,7 +165,7 @@ controls_menu::~controls_menu() game::menu::remove_text_from_ui(ctx); game::menu::delete_text(ctx); - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } } // namespace state diff --git a/src/game/state/credits.cpp b/src/game/state/credits.cpp index f8f5688..39d9a88 100644 --- a/src/game/state/credits.cpp +++ b/src/game/state/credits.cpp @@ -25,7 +25,7 @@ #include "animation/animator.hpp" #include "application.hpp" #include "scene/text.hpp" -#include "debug/logger.hpp" +#include "debug/log.hpp" namespace game { namespace state { @@ -33,7 +33,7 @@ namespace state { credits::credits(game::context& ctx): game::state::base(ctx) { - ctx.logger->push_task("Entering credits state"); + debug::log::push_task("Entering credits state"); // Construct credits text credits_text.set_material(&ctx.menu_font_material); @@ -75,17 +75,18 @@ credits::credits(game::context& ctx): credits_fade_in_animation.play(); // Set up credits skipper - ctx.input_listener->set_callback + input_mapped_subscription = ctx.input_mapper.get_input_mapped_channel().subscribe ( - [this, &ctx](const event_base& event) + [this, &ctx](const auto& event) { - auto id = event.get_event_type_id(); - if (id != mouse_moved_event::event_type_id && id != mouse_wheel_scrolled_event::event_type_id && id != gamepad_axis_moved_event::event_type_id) + auto mapping_type = event.mapping->get_mapping_type(); + + if (mapping_type != input::mapping_type::gamepad_axis && + mapping_type != input::mapping_type::mouse_motion && + mapping_type != input::mapping_type::mouse_scroll) { if (this->credits_text.get_color()[3] > 0.0f) { - ctx.input_listener->set_enabled(false); - // Change state ctx.state_machine.pop(); ctx.state_machine.emplace(new game::state::extras_menu(ctx)); @@ -93,20 +94,19 @@ credits::credits(game::context& ctx): } } ); - ctx.input_listener->set_enabled(true); + ctx.input_mapper.connect(ctx.app->get_device_manager().get_event_queue()); ctx.ui_scene->add_object(&credits_text); - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } credits::~credits() { - ctx.logger->push_task("Exiting credits state"); + debug::log::push_task("Exiting credits state"); // Disable credits skipper - ctx.input_listener->set_enabled(false); - ctx.input_listener->set_callback(nullptr); + ctx.input_mapper.disconnect(); // Destruct credits text ctx.ui_scene->remove_object(&credits_text); @@ -114,7 +114,7 @@ credits::~credits() // Destruct credits animations ctx.animator->remove_animation(&credits_fade_in_animation); - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } } // namespace state diff --git a/src/game/state/credits.hpp b/src/game/state/credits.hpp index 2156f3e..ee82a14 100644 --- a/src/game/state/credits.hpp +++ b/src/game/state/credits.hpp @@ -23,6 +23,7 @@ #include "game/state/base.hpp" #include "scene/text.hpp" #include "animation/animation.hpp" +#include "event/subscription.hpp" namespace game { namespace state { @@ -37,6 +38,7 @@ private: scene::text credits_text; animation credits_fade_in_animation; animation credits_scroll_animation; + std::shared_ptr<::event::subscription> input_mapped_subscription; }; } // namespace state diff --git a/src/game/state/extras-menu.cpp b/src/game/state/extras-menu.cpp index c5ed8cb..3b91bb2 100644 --- a/src/game/state/extras-menu.cpp +++ b/src/game/state/extras-menu.cpp @@ -22,7 +22,7 @@ #include "game/state/credits.hpp" #include "application.hpp" #include "scene/text.hpp" -#include "debug/logger.hpp" +#include "debug/log.hpp" #include "game/fonts.hpp" #include "game/menu.hpp" @@ -32,7 +32,7 @@ namespace state { extras_menu::extras_menu(game::context& ctx): game::state::base(ctx) { - ctx.logger->push_task("Entering extras menu state"); + debug::log::push_task("Entering extras menu state"); // Construct menu item texts scene::text* credits_text = new scene::text(); @@ -123,12 +123,12 @@ extras_menu::extras_menu(game::context& ctx): // Fade in menu game::menu::fade_in(ctx, nullptr); - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } extras_menu::~extras_menu() { - ctx.logger->push_task("Exiting extras menu state"); + debug::log::push_task("Exiting extras menu state"); // Destruct menu game::menu::clear_controls(ctx); @@ -137,7 +137,7 @@ extras_menu::~extras_menu() game::menu::remove_text_from_ui(ctx); game::menu::delete_text(ctx); - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } } // namespace state diff --git a/src/game/state/gamepad-config-menu.cpp b/src/game/state/gamepad-config-menu.cpp index 18f2048..c57814f 100644 --- a/src/game/state/gamepad-config-menu.cpp +++ b/src/game/state/gamepad-config-menu.cpp @@ -22,7 +22,7 @@ #include "game/context.hpp" #include "application.hpp" #include "scene/text.hpp" -#include "debug/logger.hpp" +#include "debug/log.hpp" #include "resources/resource-manager.hpp" #include "game/menu.hpp" #include "game/controls.hpp" @@ -33,7 +33,7 @@ namespace state { gamepad_config_menu::gamepad_config_menu(game::context& ctx): game::state::base(ctx) { - ctx.logger->push_task("Entering gamepad config menu state"); + debug::log::push_task("Entering gamepad config menu state"); // Add camera control menu items add_control_item("move_forward"); @@ -108,12 +108,12 @@ gamepad_config_menu::gamepad_config_menu(game::context& ctx): // Fade in menu game::menu::fade_in(ctx, nullptr); - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } gamepad_config_menu::~gamepad_config_menu() { - ctx.logger->push_task("Exiting gamepad config menu state"); + debug::log::push_task("Exiting gamepad config menu state"); // Destruct menu game::menu::clear_controls(ctx); @@ -125,13 +125,13 @@ gamepad_config_menu::~gamepad_config_menu() // Save control profile game::save_control_profile(ctx); - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } std::string gamepad_config_menu::get_binding_string(input::control* control) { std::string binding_string; - + /* auto mappings = ctx.input_event_router->get_mappings(control); for (input::mapping* mapping: *mappings) { @@ -275,14 +275,14 @@ std::string gamepad_config_menu::get_binding_string(input::control* control) } } } - + */ return binding_string; } void gamepad_config_menu::add_control_item(const std::string& control_name) { // Get pointer to control - input::control* control = ctx.controls[control_name]; + //input::control* control = ctx.controls[control_name]; // Construct texts scene::text* name_text = new scene::text(); @@ -299,9 +299,9 @@ void gamepad_config_menu::add_control_item(const std::string& control_name) name_text->set_content(control_name); // Set content of value text - value_text->set_content(get_binding_string(control)); + //value_text->set_content(get_binding_string(control)); - auto select_callback = [this, &ctx = this->ctx, control, value_text]() + auto select_callback = [this, &ctx = this->ctx, value_text]() { // Clear binding string from value text value_text->set_content((*ctx.strings)["ellipsis"]); @@ -311,6 +311,7 @@ void gamepad_config_menu::add_control_item(const std::string& control_name) // Disable controls game::menu::clear_controls(ctx); + /* // Remove gamepad event mappings from control ctx.input_event_router->remove_mappings(control, input::mapping_type::gamepad_axis); ctx.input_event_router->remove_mappings(control, input::mapping_type::gamepad_button); @@ -363,6 +364,7 @@ void gamepad_config_menu::add_control_item(const std::string& control_name) } ); ctx.input_listener->set_enabled(true); + */ }; // Register menu item callbacks diff --git a/src/game/state/graphics-menu.cpp b/src/game/state/graphics-menu.cpp index 5c83581..5830fbd 100644 --- a/src/game/state/graphics-menu.cpp +++ b/src/game/state/graphics-menu.cpp @@ -21,7 +21,7 @@ #include "game/state/options-menu.hpp" #include "application.hpp" #include "scene/text.hpp" -#include "debug/logger.hpp" +#include "debug/log.hpp" #include "game/fonts.hpp" #include "game/menu.hpp" #include "game/graphics.hpp" @@ -35,7 +35,7 @@ static void update_value_text_content(game::context* ctx); graphics_menu::graphics_menu(game::context& ctx): game::state::base(ctx) { - ctx.logger->push_task("Entering graphics menu state"); + debug::log::push_task("Entering graphics menu state"); // Construct menu item texts scene::text* fullscreen_name_text = new scene::text(); @@ -112,7 +112,7 @@ graphics_menu::graphics_menu(game::context& ctx): auto increase_resolution_callback = [this, &ctx]() { // Increase resolution - if (ctx.controls["menu_modifier"]->is_active()) + if (ctx.menu_modifier_control.is_active()) ctx.render_scale += 0.05f; else ctx.render_scale += 0.25f; @@ -136,7 +136,7 @@ graphics_menu::graphics_menu(game::context& ctx): auto decrease_resolution_callback = [this, &ctx]() { // Increase resolution - if (ctx.controls["menu_modifier"]->is_active()) + if (ctx.menu_modifier_control.is_active()) ctx.render_scale -= 0.05f; else ctx.render_scale -= 0.25f; @@ -242,7 +242,7 @@ graphics_menu::graphics_menu(game::context& ctx): auto increase_font_size_callback = [this, &ctx]() { // Increase font size - if (ctx.controls["menu_modifier"]->is_active()) + if (ctx.menu_modifier_control.is_active()) ctx.font_size += 0.01f; else ctx.font_size += 0.1f; @@ -258,16 +258,16 @@ graphics_menu::graphics_menu(game::context& ctx): (*ctx.config)["font_size"] = ctx.font_size; // Reload fonts - ctx.logger->push_task("Reloading fonts"); + debug::log::push_task("Reloading fonts"); try { game::load_fonts(ctx); } catch (...) { - ctx.logger->pop_task(EXIT_FAILURE); + debug::log::pop_task(EXIT_FAILURE); } - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); // Refresh and realign text game::menu::refresh_text(ctx); @@ -278,7 +278,7 @@ graphics_menu::graphics_menu(game::context& ctx): auto decrease_font_size_callback = [this, &ctx]() { // Decrease font size - if (ctx.controls["menu_modifier"]->is_active()) + if (ctx.menu_modifier_control.is_active()) ctx.font_size -= 0.01f; else ctx.font_size -= 0.1f; @@ -294,16 +294,16 @@ graphics_menu::graphics_menu(game::context& ctx): (*ctx.config)["font_size"] = ctx.font_size; // Reload fonts - ctx.logger->push_task("Reloading fonts"); + debug::log::push_task("Reloading fonts"); try { game::load_fonts(ctx); } catch (...) { - ctx.logger->pop_task(EXIT_FAILURE); + debug::log::pop_task(EXIT_FAILURE); } - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); // Refresh and realign text game::menu::refresh_text(ctx); @@ -322,16 +322,16 @@ graphics_menu::graphics_menu(game::context& ctx): (*ctx.config)["dyslexia_font"] = ctx.dyslexia_font; // Reload fonts - ctx.logger->push_task("Reloading fonts"); + debug::log::push_task("Reloading fonts"); try { game::load_fonts(ctx); } catch (...) { - ctx.logger->pop_task(EXIT_FAILURE); + debug::log::pop_task(EXIT_FAILURE); } - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); // Refresh and realign text game::menu::refresh_text(ctx); @@ -400,12 +400,12 @@ graphics_menu::graphics_menu(game::context& ctx): // Fade in menu game::menu::fade_in(ctx, nullptr); - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } graphics_menu::~graphics_menu() { - ctx.logger->push_task("Exiting graphics menu state"); + debug::log::push_task("Exiting graphics menu state"); // Destruct menu game::menu::clear_controls(ctx); @@ -414,7 +414,7 @@ graphics_menu::~graphics_menu() game::menu::remove_text_from_ui(ctx); game::menu::delete_text(ctx); - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } void graphics_menu::update_value_text_content() diff --git a/src/game/state/keyboard-config-menu.cpp b/src/game/state/keyboard-config-menu.cpp index a88ce9f..8e2bacf 100644 --- a/src/game/state/keyboard-config-menu.cpp +++ b/src/game/state/keyboard-config-menu.cpp @@ -21,7 +21,7 @@ #include "game/state/controls-menu.hpp" #include "application.hpp" #include "scene/text.hpp" -#include "debug/logger.hpp" +#include "debug/log.hpp" #include "resources/resource-manager.hpp" #include "game/menu.hpp" #include "game/controls.hpp" @@ -32,7 +32,7 @@ namespace state { keyboard_config_menu::keyboard_config_menu(game::context& ctx): game::state::base(ctx) { - ctx.logger->push_task("Entering keyboard config menu state"); + debug::log::push_task("Entering keyboard config menu state"); // Add camera control menu items add_control_item("move_forward"); @@ -107,12 +107,12 @@ keyboard_config_menu::keyboard_config_menu(game::context& ctx): // Fade in menu game::menu::fade_in(ctx, nullptr); - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } keyboard_config_menu::~keyboard_config_menu() { - ctx.logger->push_task("Exiting keyboard config menu state"); + debug::log::push_task("Exiting keyboard config menu state"); // Destruct menu game::menu::clear_controls(ctx); @@ -124,13 +124,13 @@ keyboard_config_menu::~keyboard_config_menu() // Save control profile game::save_control_profile(ctx); - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } std::string keyboard_config_menu::get_binding_string(input::control* control) { std::string binding_string; - + /* auto mappings = ctx.input_event_router->get_mappings(control); for (input::mapping* mapping: *mappings) { @@ -217,14 +217,14 @@ std::string keyboard_config_menu::get_binding_string(input::control* control) } } } - + */ return binding_string; } void keyboard_config_menu::add_control_item(const std::string& control_name) { // Get pointer to control - input::control* control = ctx.controls[control_name]; + //input::control* control = ctx.controls[control_name]; // Construct texts scene::text* name_text = new scene::text(); @@ -241,9 +241,9 @@ void keyboard_config_menu::add_control_item(const std::string& control_name) name_text->set_content(control_name); // Set content of value text - value_text->set_content(get_binding_string( control)); + //value_text->set_content(get_binding_string( control)); - auto select_callback = [this, &ctx = this->ctx, control, value_text]() + auto select_callback = [this, &ctx = this->ctx, value_text]() { // Clear binding string from value text value_text->set_content((*ctx.strings)["ellipsis"]); @@ -253,6 +253,7 @@ void keyboard_config_menu::add_control_item(const std::string& control_name) // Disable controls game::menu::clear_controls(ctx); + /* // Remove keyboard and mouse event mappings from control ctx.input_event_router->remove_mappings(control, input::mapping_type::key); ctx.input_event_router->remove_mappings(control, input::mapping_type::mouse_motion); @@ -317,6 +318,7 @@ void keyboard_config_menu::add_control_item(const std::string& control_name) } ); ctx.input_listener->set_enabled(true); + */ }; // Register menu item callbacks diff --git a/src/game/state/language-menu.cpp b/src/game/state/language-menu.cpp index 8698b59..e070e8e 100644 --- a/src/game/state/language-menu.cpp +++ b/src/game/state/language-menu.cpp @@ -21,7 +21,7 @@ #include "game/state/options-menu.hpp" #include "application.hpp" #include "scene/text.hpp" -#include "debug/logger.hpp" +#include "debug/log.hpp" #include "game/fonts.hpp" #include "game/menu.hpp" @@ -31,7 +31,7 @@ namespace state { language_menu::language_menu(game::context& ctx): game::state::base(ctx) { - ctx.logger->push_task("Entering language menu state"); + debug::log::push_task("Entering language menu state"); // Construct menu item texts scene::text* language_name_text = new scene::text(); @@ -70,19 +70,19 @@ language_menu::language_menu(game::context& ctx): // Update language in config (*ctx.config)["language"] = ctx.language_code; - ctx.logger->log("Language changed to \"" + ctx.language_code + "\""); + debug::log::info("Language changed to \"" + ctx.language_code + "\""); // Reload fonts - ctx.logger->push_task("Reloading fonts"); + debug::log::push_task("Reloading fonts"); try { game::load_fonts(ctx); } catch (...) { - ctx.logger->pop_task(EXIT_FAILURE); + debug::log::pop_task(EXIT_FAILURE); } - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); game::menu::update_text_font(ctx); this->update_text_content(); @@ -104,19 +104,19 @@ language_menu::language_menu(game::context& ctx): // Update language in config (*ctx.config)["language"] = ctx.language_code; - ctx.logger->log("Language changed to \"" + ctx.language_code + "\""); + debug::log::info("Language changed to \"" + ctx.language_code + "\""); // Reload fonts - ctx.logger->push_task("Reloading fonts"); + debug::log::push_task("Reloading fonts"); try { game::load_fonts(ctx); } catch (...) { - ctx.logger->pop_task(EXIT_FAILURE); + debug::log::pop_task(EXIT_FAILURE); } - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); game::menu::update_text_font(ctx); this->update_text_content(); @@ -168,12 +168,12 @@ language_menu::language_menu(game::context& ctx): // Fade in menu game::menu::fade_in(ctx, nullptr); - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } language_menu::~language_menu() { - ctx.logger->push_task("Exiting language menu state"); + debug::log::push_task("Exiting language menu state"); // Destruct menu game::menu::clear_controls(ctx); @@ -182,7 +182,7 @@ language_menu::~language_menu() game::menu::remove_text_from_ui(ctx); game::menu::delete_text(ctx); - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } void language_menu::update_text_content() diff --git a/src/game/state/main-menu.cpp b/src/game/state/main-menu.cpp index cd6a5bf..6644028 100644 --- a/src/game/state/main-menu.cpp +++ b/src/game/state/main-menu.cpp @@ -43,9 +43,6 @@ #include "game/component/transform.hpp" #include "math/projection.hpp" #include -#include - -#include "event/signal.hpp" namespace game { namespace state { @@ -53,42 +50,7 @@ namespace state { main_menu::main_menu(game::context& ctx, bool fade_in): game::state::base(ctx) { - ctx.logger->push_task("Entering main menu state"); - - viewport_size_connection = ctx.app->get_viewport_size_signal().connect - ( - [](int w, int h) - { - std::cout << "viewport resized " << w << "x" << h << std::endl; - } - ); - - window_motion_connection = ctx.app->get_window_motion_signal().connect - ( - [](int x, int y) - { - std::cout << "window moved to " << x << ", " << y << std::endl; - } - ); - - window_focus_connection = ctx.app->get_window_focus_signal().connect - ( - [](bool focus) - { - if (focus) - std::cout << "focus gained" << std::endl; - else - std::cout << "focus lost" << std::endl; - } - ); - - window_close_connection = ctx.app->get_window_close_signal().connect - ( - []() - { - std::cout << "window closed" << std::endl; - } - ); + debug::log::push_task("Entering main menu state"); ctx.ui_clear_pass->set_cleared_buffers(true, true, false); @@ -320,12 +282,12 @@ main_menu::main_menu(game::context& ctx, bool fade_in): //if (!ctx.menu_bg_billboard->is_active()) // game::menu::fade_in_bg(ctx); - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } main_menu::~main_menu() { - ctx.logger->push_task("Exiting main menu state"); + debug::log::push_task("Exiting main menu state"); // Destruct menu game::menu::clear_controls(ctx); @@ -343,7 +305,7 @@ main_menu::~main_menu() // Destruct title text ctx.ui_scene->remove_object(&title_text); - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } void main_menu::fade_in_title() diff --git a/src/game/state/main-menu.hpp b/src/game/state/main-menu.hpp index b7f30a5..b41a5b2 100644 --- a/src/game/state/main-menu.hpp +++ b/src/game/state/main-menu.hpp @@ -24,7 +24,6 @@ #include "scene/text.hpp" #include "animation/animation.hpp" #include "entity/id.hpp" -#include "event/signal.hpp" namespace game { namespace state { @@ -43,11 +42,6 @@ private: animation title_fade_animation; entity::id swarm_eid; - - std::shared_ptr window_close_connection; - std::shared_ptr window_motion_connection; - std::shared_ptr window_focus_connection; - std::shared_ptr viewport_size_connection; }; } // namespace state diff --git a/src/game/state/nest-selection.cpp b/src/game/state/nest-selection.cpp index f87a869..a001111 100644 --- a/src/game/state/nest-selection.cpp +++ b/src/game/state/nest-selection.cpp @@ -51,7 +51,6 @@ #include "application.hpp" #include "input/mouse.hpp" #include "math/projection.hpp" -#include #include "game/ant/morphogenesis.hpp" #include "game/ant/phenome.hpp" @@ -67,20 +66,20 @@ namespace state { nest_selection::nest_selection(game::context& ctx): game::state::base(ctx) { - ctx.logger->push_task("Entering nest selection state"); + debug::log::push_task("Entering nest selection state"); - ctx.logger->push_task("Generating genome"); + debug::log::push_task("Generating genome"); std::random_device rng; ant::genome* genome = ant::cladogenesis(ctx.active_ecoregion->gene_pools[0], rng); - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); - ctx.logger->push_task("Building worker phenome"); + debug::log::push_task("Building worker phenome"); ant::phenome worker_phenome = ant::phenome(*genome, ant::caste::worker); - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); - ctx.logger->push_task("Generating worker model"); + debug::log::push_task("Generating worker model"); render::model* worker_model = ant::morphogenesis(worker_phenome); - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); // Create worker entity(s) entity::id worker_eid = ctx.entity_registry->create(); @@ -199,16 +198,16 @@ nest_selection::nest_selection(game::context& ctx): // Queue control setup ctx.function_queue.push(std::bind(&nest_selection::enable_controls, this)); - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } nest_selection::~nest_selection() { - ctx.logger->push_task("Exiting nest selection state"); + debug::log::push_task("Exiting nest selection state"); destroy_first_person_camera_rig(); - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } void nest_selection::create_first_person_camera_rig() @@ -383,6 +382,7 @@ void nest_selection::satisfy_first_person_camera_rig_constraints() void nest_selection::enable_controls() { + /* // Reset mouse look mouse_look = false; @@ -715,7 +715,7 @@ void nest_selection::enable_controls() { //ctx.astronomy_system->set_exposure_offset(ctx.astronomy_system->get_exposure_offset() - 1.0f); ctx.surface_camera->set_exposure(ctx.surface_camera->get_exposure() + 2.0f * static_cast(ctx.loop.get_update_period())); - ctx.logger->log("EV100: " + std::to_string(ctx.surface_camera->get_exposure())); + debug::log::info("EV100: " + std::to_string(ctx.surface_camera->get_exposure())); } ); @@ -725,13 +725,15 @@ void nest_selection::enable_controls() { //ctx.astronomy_system->set_exposure_offset(ctx.astronomy_system->get_exposure_offset() + 1.0f); ctx.surface_camera->set_exposure(ctx.surface_camera->get_exposure() - 2.0f * static_cast(ctx.loop.get_update_period())); - ctx.logger->log("EV100: " + std::to_string(ctx.surface_camera->get_exposure())); + debug::log::info("EV100: " + std::to_string(ctx.surface_camera->get_exposure())); } ); + */ } void nest_selection::disable_controls() { + /* if (mouse_look) { mouse_look = false; @@ -763,6 +765,7 @@ void nest_selection::disable_controls() ctx.controls["pause"]->set_activated_callback(nullptr); ctx.controls["increase_exposure"]->set_active_callback(nullptr); ctx.controls["decrease_exposure"]->set_active_callback(nullptr); + */ } } // namespace state diff --git a/src/game/state/nuptial-flight.cpp b/src/game/state/nuptial-flight.cpp index fc63ecf..3dc22ff 100644 --- a/src/game/state/nuptial-flight.cpp +++ b/src/game/state/nuptial-flight.cpp @@ -54,7 +54,6 @@ #include "color/color.hpp" #include "application.hpp" #include "input/mouse.hpp" -#include namespace game { namespace state { @@ -62,7 +61,7 @@ namespace state { nuptial_flight::nuptial_flight(game::context& ctx): game::state::base(ctx) { - ctx.logger->push_task("Entering nuptial flight state"); + debug::log::push_task("Entering nuptial flight state"); // Init selected picking flag selected_picking_flag = std::uint32_t{1} << (sizeof(std::uint32_t) * 8 - 1); @@ -144,12 +143,12 @@ nuptial_flight::nuptial_flight(game::context& ctx): // Queue control setup ctx.function_queue.push(std::bind(&nuptial_flight::enable_controls, this)); - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } nuptial_flight::~nuptial_flight() { - ctx.logger->push_task("Exiting nuptial flight state"); + debug::log::push_task("Exiting nuptial flight state"); // Deselect selected entity select_entity(entt::null); @@ -157,7 +156,7 @@ nuptial_flight::~nuptial_flight() destroy_camera_rig(); game::ant::destroy_swarm(ctx, swarm_eid); - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } void nuptial_flight::create_camera_rig() @@ -389,6 +388,7 @@ void nuptial_flight::satisfy_camera_rig_constraints() void nuptial_flight::enable_controls() { + /* // Reset mouse look mouse_look = false; @@ -747,7 +747,7 @@ void nuptial_flight::enable_controls() { //ctx.astronomy_system->set_exposure_offset(ctx.astronomy_system->get_exposure_offset() - 1.0f); ctx.surface_camera->set_exposure(ctx.surface_camera->get_exposure() + 3.0f * static_cast(ctx.loop.get_update_period())); - ctx.logger->log("EV100: " + std::to_string(ctx.surface_camera->get_exposure())); + debug::log::info("EV100: " + std::to_string(ctx.surface_camera->get_exposure())); } ); @@ -757,13 +757,15 @@ void nuptial_flight::enable_controls() { //ctx.astronomy_system->set_exposure_offset(ctx.astronomy_system->get_exposure_offset() + 1.0f); ctx.surface_camera->set_exposure(ctx.surface_camera->get_exposure() - 3.0f * static_cast(ctx.loop.get_update_period())); - ctx.logger->log("EV100: " + std::to_string(ctx.surface_camera->get_exposure())); + debug::log::info("EV100: " + std::to_string(ctx.surface_camera->get_exposure())); } ); + */ } void nuptial_flight::disable_controls() { + /* if (mouse_look) { mouse_look = false; @@ -795,6 +797,7 @@ void nuptial_flight::disable_controls() ctx.controls["pause"]->set_activated_callback(nullptr); ctx.controls["increase_exposure"]->set_active_callback(nullptr); ctx.controls["decrease_exposure"]->set_active_callback(nullptr); + */ } void nuptial_flight::select_entity(entity::id entity_id) diff --git a/src/game/state/options-menu.cpp b/src/game/state/options-menu.cpp index 4ca2895..738322e 100644 --- a/src/game/state/options-menu.cpp +++ b/src/game/state/options-menu.cpp @@ -31,7 +31,7 @@ #include "animation/animator.hpp" #include "application.hpp" #include "scene/text.hpp" -#include "debug/logger.hpp" +#include "debug/log.hpp" namespace game { namespace state { @@ -39,7 +39,7 @@ namespace state { options_menu::options_menu(game::context& ctx): game::state::base(ctx) { - ctx.logger->push_task("Entering options menu state"); + debug::log::push_task("Entering options menu state"); // Construct menu item texts scene::text* controls_text = new scene::text(); @@ -216,12 +216,12 @@ options_menu::options_menu(game::context& ctx): // Fade in menu game::menu::fade_in(ctx, nullptr); - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } options_menu::~options_menu() { - ctx.logger->push_task("Exiting options menu state"); + debug::log::push_task("Exiting options menu state"); // Destruct menu game::menu::clear_controls(ctx); @@ -230,7 +230,7 @@ options_menu::~options_menu() game::menu::remove_text_from_ui(ctx); game::menu::delete_text(ctx); - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } } // namespace state diff --git a/src/game/state/pause-menu.cpp b/src/game/state/pause-menu.cpp index 3581cef..93e9ef9 100644 --- a/src/game/state/pause-menu.cpp +++ b/src/game/state/pause-menu.cpp @@ -27,7 +27,7 @@ #include "animation/animator.hpp" #include "application.hpp" #include "scene/text.hpp" -#include "debug/logger.hpp" +#include "debug/log.hpp" #include "animation/screen-transition.hpp" #include "config.hpp" #include "game/save.hpp" @@ -38,7 +38,7 @@ namespace state { pause_menu::pause_menu(game::context& ctx): game::state::base(ctx) { - ctx.logger->push_task("Entering pause menu state"); + debug::log::push_task("Entering pause menu state"); // Construct menu item texts scene::text* resume_text = new scene::text(); @@ -72,7 +72,7 @@ pause_menu::pause_menu(game::context& ctx): auto select_resume_callback = [&ctx]() { // Disable unpause control - ctx.controls["pause"]->set_activated_callback(nullptr); + //ctx.controls["pause"]->set_activated_callback(nullptr); // Disable menu controls game::menu::clear_controls(ctx); @@ -96,7 +96,7 @@ pause_menu::pause_menu(game::context& ctx): auto select_options_callback = [&ctx]() { // Disable unpause control - ctx.controls["pause"]->set_activated_callback(nullptr); + //ctx.controls["pause"]->set_activated_callback(nullptr); // Disable menu controls game::menu::clear_controls(ctx); @@ -122,7 +122,7 @@ pause_menu::pause_menu(game::context& ctx): auto select_main_menu_callback = [&ctx]() { // Disable unpause control - ctx.controls["pause"]->set_activated_callback(nullptr); + //ctx.controls["pause"]->set_activated_callback(nullptr); // Disable menu controls game::menu::clear_controls(ctx); @@ -155,7 +155,7 @@ pause_menu::pause_menu(game::context& ctx): auto select_quit_callback = [&ctx]() { // Disable unpause control - ctx.controls["pause"]->set_activated_callback(nullptr); + //ctx.controls["pause"]->set_activated_callback(nullptr); // Disable menu controls game::menu::clear_controls(ctx); @@ -192,7 +192,7 @@ pause_menu::pause_menu(game::context& ctx): [&ctx, select_resume_callback]() { // Enable unpause control - ctx.controls["pause"]->set_activated_callback(select_resume_callback); + //ctx.controls["pause"]->set_activated_callback(select_resume_callback); // Enable menu controls game::menu::setup_controls(ctx); @@ -207,12 +207,12 @@ pause_menu::pause_menu(game::context& ctx): // Save colony game::save::colony(ctx); - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } pause_menu::~pause_menu() { - ctx.logger->push_task("Exiting pause menu state"); + debug::log::push_task("Exiting pause menu state"); // Destruct menu game::menu::clear_controls(ctx); @@ -221,7 +221,7 @@ pause_menu::~pause_menu() game::menu::remove_text_from_ui(ctx); game::menu::delete_text(ctx); - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } } // namespace state diff --git a/src/game/state/sound-menu.cpp b/src/game/state/sound-menu.cpp index 35146de..b6f00f5 100644 --- a/src/game/state/sound-menu.cpp +++ b/src/game/state/sound-menu.cpp @@ -21,7 +21,7 @@ #include "game/state/options-menu.hpp" #include "application.hpp" #include "scene/text.hpp" -#include "debug/logger.hpp" +#include "debug/log.hpp" #include "game/menu.hpp" namespace game { @@ -30,7 +30,7 @@ namespace state { sound_menu::sound_menu(game::context& ctx): game::state::base(ctx) { - ctx.logger->push_task("Entering sound menu state"); + debug::log::push_task("Entering sound menu state"); // Construct menu item texts scene::text* master_volume_name_text = new scene::text(); @@ -80,7 +80,7 @@ sound_menu::sound_menu(game::context& ctx): auto increase_volume_callback = [this, &ctx](float* volume) { // Increase volume - if (ctx.controls["menu_modifier"]->is_active()) + if (ctx.menu_modifier_control.is_active()) *volume += 0.01f; else *volume += 0.1f; @@ -96,7 +96,7 @@ sound_menu::sound_menu(game::context& ctx): auto decrease_volume_callback = [this, &ctx](float* volume) { // Decrease volume - if (ctx.controls["menu_modifier"]->is_active()) + if (ctx.menu_modifier_control.is_active()) *volume -= 0.01f; else *volume -= 0.1f; @@ -131,7 +131,7 @@ sound_menu::sound_menu(game::context& ctx): auto increase_captions_size_callback = [this, &ctx]() { // Increase size - if (ctx.controls["menu_modifier"]->is_active()) + if (ctx.menu_modifier_control.is_active()) ctx.captions_size += 0.01f; else ctx.captions_size += 0.1f; @@ -148,7 +148,7 @@ sound_menu::sound_menu(game::context& ctx): auto decrease_captions_size_callback = [this, &ctx]() { // Decrease size - if (ctx.controls["menu_modifier"]->is_active()) + if (ctx.menu_modifier_control.is_active()) ctx.captions_size -= 0.01f; else ctx.captions_size -= 0.1f; @@ -220,12 +220,12 @@ sound_menu::sound_menu(game::context& ctx): // Fade in menu game::menu::fade_in(ctx, nullptr); - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } sound_menu::~sound_menu() { - ctx.logger->push_task("Exiting sound menu state"); + debug::log::push_task("Exiting sound menu state"); // Destruct menu game::menu::clear_controls(ctx); @@ -242,7 +242,7 @@ sound_menu::~sound_menu() (*ctx.config)["captions"] = ctx.captions; (*ctx.config)["captions_size"] = ctx.captions_size; - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } void sound_menu::update_value_text_content() diff --git a/src/game/state/splash.cpp b/src/game/state/splash.cpp index 07ad937..413db38 100644 --- a/src/game/state/splash.cpp +++ b/src/game/state/splash.cpp @@ -26,7 +26,7 @@ #include "application.hpp" #include "render/passes/clear-pass.hpp" #include "game/context.hpp" -#include "debug/logger.hpp" +#include "debug/log.hpp" #include "resources/resource-manager.hpp" #include "render/material-flags.hpp" #include "math/linear-algebra.hpp" @@ -35,9 +35,10 @@ namespace game { namespace state { splash::splash(game::context& ctx): - game::state::base(ctx) + game::state::base(ctx), + skipped(false) { - ctx.logger->push_task("Entering splash state"); + debug::log::push_task("Entering splash state"); // Enable color buffer clearing in UI pass ctx.ui_clear_pass->set_cleared_buffers(true, true, false); @@ -131,36 +132,47 @@ splash::splash(game::context& ctx): splash_fade_in_animation.play(); // Set up splash skipper - ctx.input_listener->set_callback + input_mapped_subscription = ctx.input_mapper.get_input_mapped_channel().subscribe ( - [&ctx](const event_base& event) + [this](const auto& event) { - auto id = event.get_event_type_id(); - if (id != mouse_moved_event::event_type_id && id != mouse_wheel_scrolled_event::event_type_id && id != gamepad_axis_moved_event::event_type_id) + auto mapping_type = event.mapping->get_mapping_type(); + + if (!this->skipped && + mapping_type != input::mapping_type::gamepad_axis && + mapping_type != input::mapping_type::mouse_motion && + mapping_type != input::mapping_type::mouse_scroll) { - // Black out screen - ctx.rasterizer->set_clear_color(0.0f, 0.0f, 0.0f, 1.0f); - ctx.rasterizer->clear_framebuffer(true, false, false); - ctx.app->swap_buffers(); + this->skipped = true; - // Change to main menu state - ctx.state_machine.pop(); - ctx.state_machine.emplace(new game::state::main_menu(ctx, true)); + this->ctx.function_queue.emplace + ( + [&ctx = this->ctx]() + { + // Black out screen + ctx.rasterizer->set_clear_color(0.0f, 0.0f, 0.0f, 1.0f); + ctx.rasterizer->clear_framebuffer(true, false, false); + ctx.app->swap_buffers(); + + // Change to main menu state + ctx.state_machine.pop(); + ctx.state_machine.emplace(new game::state::main_menu(ctx, true)); + } + ); } } ); - ctx.input_listener->set_enabled(true); + ctx.input_mapper.connect(ctx.app->get_device_manager().get_event_queue()); - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } splash::~splash() { - ctx.logger->push_task("Exiting splash state"); + debug::log::push_task("Exiting splash state"); // Disable splash skipper - ctx.input_listener->set_enabled(false); - ctx.input_listener->set_callback(nullptr); + ctx.input_mapper.disconnect(); // Remove splash fade animations from animator ctx.animator->remove_animation(&splash_fade_in_animation); @@ -175,7 +187,7 @@ splash::~splash() // Disable color buffer clearing in UI pass ctx.ui_clear_pass->set_cleared_buffers(false, true, false); - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } } // namespace state diff --git a/src/game/state/splash.hpp b/src/game/state/splash.hpp index 30e07f9..9f6dbfe 100644 --- a/src/game/state/splash.hpp +++ b/src/game/state/splash.hpp @@ -24,6 +24,7 @@ #include "render/material.hpp" #include "scene/billboard.hpp" #include "animation/animation.hpp" +#include "event/subscription.hpp" namespace game { namespace state { @@ -39,6 +40,8 @@ private: scene::billboard splash_billboard; animation splash_fade_in_animation; animation splash_fade_out_animation; + std::shared_ptr<::event::subscription> input_mapped_subscription; + bool skipped; }; } // namespace state diff --git a/src/game/system/astronomy.cpp b/src/game/system/astronomy.cpp index 9f52d83..aeec70b 100644 --- a/src/game/system/astronomy.cpp +++ b/src/game/system/astronomy.cpp @@ -36,7 +36,6 @@ #include "astro/apparent-size.hpp" #include "geom/solid-angle.hpp" #include "math/polynomial.hpp" -#include namespace game { namespace system { diff --git a/src/game/system/camera.cpp b/src/game/system/camera.cpp index 72b76d0..4282e13 100644 --- a/src/game/system/camera.cpp +++ b/src/game/system/camera.cpp @@ -37,10 +37,5 @@ void camera::set_viewport(const float4& viewport) this->viewport = viewport; } -void camera::handle_event(const window_resized_event& event) -{ - set_viewport({0.0f, 0.0f, static_cast(event.w), static_cast(event.h)}); -} - } // namespace system } // namespace game diff --git a/src/game/system/camera.hpp b/src/game/system/camera.hpp index e5b122d..ba8badf 100644 --- a/src/game/system/camera.hpp +++ b/src/game/system/camera.hpp @@ -21,17 +21,12 @@ #define ANTKEEPER_GAME_SYSTEM_CAMERA_HPP #include "game/system/updatable.hpp" -#include "event/event-handler.hpp" -#include "event/input-events.hpp" -#include "event/window-events.hpp" #include "utility/fundamental-types.hpp" namespace game { namespace system { -class camera: - public updatable, - public event_handler +class camera: public updatable { public: camera(entity::registry& registry); @@ -40,8 +35,6 @@ public: void set_viewport(const float4& viewport); private: - virtual void handle_event(const window_resized_event& event); - float4 viewport; }; diff --git a/src/game/system/orbit.cpp b/src/game/system/orbit.cpp index 1867cd2..f6d2428 100644 --- a/src/game/system/orbit.cpp +++ b/src/game/system/orbit.cpp @@ -19,7 +19,6 @@ #include "game/system/orbit.hpp" #include "physics/orbit/orbit.hpp" -#include namespace game { namespace system { diff --git a/src/game/system/render.cpp b/src/game/system/render.cpp index 3e44cfc..445ecdc 100644 --- a/src/game/system/render.cpp +++ b/src/game/system/render.cpp @@ -24,7 +24,6 @@ #include "scene/directional-light.hpp" #include "scene/ambient-light.hpp" #include "scene/spot-light.hpp" -#include namespace game { namespace system { diff --git a/src/game/system/spatial.cpp b/src/game/system/spatial.cpp index edf9ffe..e6941e1 100644 --- a/src/game/system/spatial.cpp +++ b/src/game/system/spatial.cpp @@ -20,7 +20,6 @@ #include "spatial.hpp" #include "game/component/transform.hpp" #include "game/component/constraint-stack.hpp" -#include namespace game { namespace system { diff --git a/src/game/system/terrain.cpp b/src/game/system/terrain.cpp index 3eddef3..31e4c03 100644 --- a/src/game/system/terrain.cpp +++ b/src/game/system/terrain.cpp @@ -31,7 +31,6 @@ #include "utility/fundamental-types.hpp" #include "math/compile.hpp" #include -#include namespace game { namespace system { diff --git a/src/game/world.cpp b/src/game/world.cpp index d21cb98..235be3f 100644 --- a/src/game/world.cpp +++ b/src/game/world.cpp @@ -20,7 +20,7 @@ #include "application.hpp" #include "color/color.hpp" #include "config.hpp" -#include "debug/logger.hpp" +#include "debug/log.hpp" #include "entity/archetype.hpp" #include "entity/commands.hpp" #include "game/component/atmosphere.hpp" @@ -68,7 +68,6 @@ #include #include #include -#include #include namespace game { @@ -94,19 +93,19 @@ static void create_moon(game::context& ctx); void cosmogenesis(game::context& ctx) { - ctx.logger->push_task("Generating cosmos"); + debug::log::push_task("Generating cosmos"); load_ephemeris(ctx); create_stars(ctx); create_sun(ctx); create_earth_moon_system(ctx); - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } void create_observer(game::context& ctx) { - ctx.logger->push_task("Creating observer"); + debug::log::push_task("Creating observer"); try { @@ -139,11 +138,11 @@ void create_observer(game::context& ctx) } catch (const std::exception&) { - ctx.logger->pop_task(EXIT_FAILURE); + debug::log::pop_task(EXIT_FAILURE); return; } - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } void set_location(game::context& ctx, double elevation, double latitude, double longitude) @@ -171,7 +170,7 @@ void set_location(game::context& ctx, double elevation, double latitude, double void set_time(game::context& ctx, double t) { - ctx.logger->push_task("Setting time to UT1 " + std::to_string(t)); + debug::log::push_task("Setting time to UT1 " + std::to_string(t)); try { ctx.astronomy_system->set_time(t); @@ -179,10 +178,10 @@ void set_time(game::context& ctx, double t) } catch (const std::exception&) { - ctx.logger->pop_task(EXIT_FAILURE); + debug::log::pop_task(EXIT_FAILURE); return; } - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } void set_time(game::context& ctx, int year, int month, int day, int hour, int minute, double second) @@ -221,7 +220,7 @@ void set_time_scale(game::context& ctx, double scale) void load_ephemeris(game::context& ctx) { - ctx.logger->push_task("Loading ephemeris"); + debug::log::push_task("Loading ephemeris"); try { @@ -232,8 +231,8 @@ void load_ephemeris(game::context& ctx) } else { - ctx.logger->warning("No ephemeris set in config"); - ctx.logger->pop_task(EXIT_FAILURE); + debug::log::warning("No ephemeris set in config"); + debug::log::pop_task(EXIT_FAILURE); return; } @@ -241,16 +240,16 @@ void load_ephemeris(game::context& ctx) } catch (const std::exception&) { - ctx.logger->pop_task(EXIT_FAILURE); + debug::log::pop_task(EXIT_FAILURE); return; } - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } void create_stars(game::context& ctx) { - ctx.logger->push_task("Generating fixed stars"); + debug::log::push_task("Generating fixed stars"); // Load star catalog string_table* star_catalog = nullptr; @@ -263,8 +262,8 @@ void create_stars(game::context& ctx) } else { - ctx.logger->warning("No star catalog set in config"); - ctx.logger->pop_task(EXIT_FAILURE); + debug::log::warning("No star catalog set in config"); + debug::log::pop_task(EXIT_FAILURE); return; } @@ -272,7 +271,7 @@ void create_stars(game::context& ctx) } catch (const std::exception&) { - ctx.logger->pop_task(EXIT_FAILURE); + debug::log::pop_task(EXIT_FAILURE); return; } @@ -307,7 +306,7 @@ void create_stars(game::context& ctx) } catch (const std::exception&) { - ctx.logger->warning("Invalid star catalog item on row " + std::to_string(i)); + debug::log::warning("Invalid star catalog item on row " + std::to_string(i)); } // Convert right ascension and declination from degrees to radians @@ -401,12 +400,12 @@ void create_stars(game::context& ctx) // Pass starlight illuminance to astronomy system ctx.astronomy_system->set_starlight_illuminance(starlight_illuminance); - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } void create_sun(game::context& ctx) { - ctx.logger->push_task("Generating Sun"); + debug::log::push_task("Generating Sun"); try { @@ -449,16 +448,16 @@ void create_sun(game::context& ctx) } catch (const std::exception&) { - ctx.logger->pop_task(EXIT_FAILURE); + debug::log::pop_task(EXIT_FAILURE); return; } - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } void create_earth_moon_system(game::context& ctx) { - ctx.logger->push_task("Generating Earth-Moon system"); + debug::log::push_task("Generating Earth-Moon system"); try { @@ -475,16 +474,16 @@ void create_earth_moon_system(game::context& ctx) } catch (const std::exception&) { - ctx.logger->pop_task(EXIT_FAILURE); + debug::log::pop_task(EXIT_FAILURE); return; } - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } void create_earth(game::context& ctx) { - ctx.logger->push_task("Generating Earth"); + debug::log::push_task("Generating Earth"); try { @@ -498,16 +497,16 @@ void create_earth(game::context& ctx) } catch (const std::exception&) { - ctx.logger->pop_task(EXIT_FAILURE); + debug::log::pop_task(EXIT_FAILURE); return; } - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } void create_moon(game::context& ctx) { - ctx.logger->push_task("Generating Moon"); + debug::log::push_task("Generating Moon"); try { @@ -535,11 +534,11 @@ void create_moon(game::context& ctx) } catch (const std::exception&) { - ctx.logger->pop_task(EXIT_FAILURE); + debug::log::pop_task(EXIT_FAILURE); return; } - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } void enter_ecoregion(game::context& ctx, const ecoregion& ecoregion) @@ -600,7 +599,7 @@ void enter_ecoregion(game::context& ctx, const ecoregion& ecoregion) */ - ctx.logger->push_task("Entering ecoregion " + ecoregion.name); + debug::log::push_task("Entering ecoregion " + ecoregion.name); try { // Set active ecoregion @@ -644,9 +643,9 @@ void enter_ecoregion(game::context& ctx, const ecoregion& ecoregion) } catch (...) { - ctx.logger->pop_task(EXIT_FAILURE); + debug::log::pop_task(EXIT_FAILURE); } - ctx.logger->pop_task(EXIT_SUCCESS); + debug::log::pop_task(EXIT_SUCCESS); } } // namespace world diff --git a/src/input/control-map.cpp b/src/input/control-map.cpp new file mode 100644 index 0000000..74d955d --- /dev/null +++ b/src/input/control-map.cpp @@ -0,0 +1,299 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "input/control-map.hpp" +#include +#include +#include +#include + +namespace input { + +void control_map::connect(::event::queue& queue) +{ + subscriptions.emplace_back(queue.subscribe(std::bind_front(&control_map::handle_gamepad_axis_moved, this))); + subscriptions.emplace_back(queue.subscribe(std::bind_front(&control_map::handle_gamepad_button_pressed, this))); + subscriptions.emplace_back(queue.subscribe(std::bind_front(&control_map::handle_gamepad_button_released, this))); + subscriptions.emplace_back(queue.subscribe(std::bind_front(&control_map::handle_key_pressed, this))); + subscriptions.emplace_back(queue.subscribe(std::bind_front(&control_map::handle_key_released, this))); + subscriptions.emplace_back(queue.subscribe(std::bind_front(&control_map::handle_mouse_button_pressed, this))); + subscriptions.emplace_back(queue.subscribe(std::bind_front(&control_map::handle_mouse_button_released, this))); + subscriptions.emplace_back(queue.subscribe(std::bind_front(&control_map::handle_mouse_moved, this))); + subscriptions.emplace_back(queue.subscribe(std::bind_front(&control_map::handle_mouse_scrolled, this))); +} + +void control_map::disconnect() +{ + subscriptions.clear(); +} + +void control_map::add_mapping(control& control, const mapping& mapping) +{ + switch (mapping.get_mapping_type()) + { + case mapping_type::gamepad_axis: + add_mapping(control, static_cast(mapping)); + break; + + case mapping_type::gamepad_button: + add_mapping(control, static_cast(mapping)); + break; + + case mapping_type::key: + add_mapping(control, static_cast(mapping)); + break; + + case mapping_type::mouse_button: + add_mapping(control, static_cast(mapping)); + break; + + case mapping_type::mouse_motion: + add_mapping(control, static_cast(mapping)); + break; + + case mapping_type::mouse_scroll: + add_mapping(control, static_cast(mapping)); + break; + + default: + //std::unreachable(); + break; + } +} + +void control_map::add_mapping(control& control, gamepad_axis_mapping&& mapping) +{ + gamepad_axis_mappings.emplace_back(&control, mapping); +} + +void control_map::add_mapping(control& control, gamepad_button_mapping&& mapping) +{ + gamepad_button_mappings.emplace_back(&control, mapping); +} + +void control_map::add_mapping(control& control, key_mapping&& mapping) +{ + key_mappings.emplace_back(&control, mapping); +} + +void control_map::add_mapping(control& control, mouse_button_mapping&& mapping) +{ + mouse_button_mappings.emplace_back(&control, mapping); +} + +void control_map::add_mapping(control& control, mouse_motion_mapping&& mapping) +{ + mouse_motion_mappings.emplace_back(&control, mapping); +} + +void control_map::add_mapping(control& control, mouse_scroll_mapping&& mapping) +{ + mouse_scroll_mappings.emplace_back(&control, mapping); +} + +void control_map::remove_mappings(control& control, mapping_type type) +{ + auto predicate = [&](const auto& tuple) -> bool + { + return std::get<0>(tuple) == &control; + }; + + switch (type) + { + case mapping_type::gamepad_axis: + std::erase_if(gamepad_axis_mappings, predicate); + break; + + case mapping_type::gamepad_button: + std::erase_if(gamepad_button_mappings, predicate); + break; + + case mapping_type::key: + std::erase_if(key_mappings, predicate); + break; + + case mapping_type::mouse_button: + std::erase_if(mouse_button_mappings, predicate); + break; + + case mapping_type::mouse_motion: + std::erase_if(mouse_motion_mappings, predicate); + break; + + case mapping_type::mouse_scroll: + std::erase_if(mouse_scroll_mappings, predicate); + break; + + default: + //std::unreachable(); + break; + } +} + +void control_map::remove_mappings(control& control) +{ + auto predicate = [&](const auto& tuple) -> bool + { + return std::get<0>(tuple) == &control; + }; + + std::erase_if(gamepad_axis_mappings, predicate); + std::erase_if(gamepad_button_mappings, predicate); + std::erase_if(key_mappings, predicate); + std::erase_if(mouse_button_mappings, predicate); + std::erase_if(mouse_motion_mappings, predicate); + std::erase_if(mouse_scroll_mappings, predicate); +} + +void control_map::remove_mappings() +{ + gamepad_axis_mappings.clear(); + gamepad_button_mappings.clear(); + key_mappings.clear(); + mouse_button_mappings.clear(); + mouse_motion_mappings.clear(); + mouse_scroll_mappings.clear(); +} + +void control_map::handle_gamepad_axis_moved(const event::gamepad_axis_moved& event) +{ + for (const auto& [control, mapping]: gamepad_axis_mappings) + { + if (mapping.axis == event.axis && + (!mapping.gamepad || mapping.gamepad == event.gamepad)) + { + if (std::signbit(event.position) == mapping.direction) + { + control->evaluate(std::abs(event.position)); + } + else + { + control->evaluate(0.0f); + } + } + } +} + +void control_map::handle_gamepad_button_pressed(const event::gamepad_button_pressed& event) +{ + for (const auto& [control, mapping]: gamepad_button_mappings) + { + if (mapping.button == event.button && + (!mapping.gamepad || mapping.gamepad == event.gamepad)) + { + control->evaluate(1.0f); + } + } +} + +void control_map::handle_gamepad_button_released(const event::gamepad_button_released& event) +{ + for (const auto& [control, mapping]: gamepad_button_mappings) + { + if (mapping.button == event.button && + (!mapping.gamepad || mapping.gamepad == event.gamepad)) + { + control->evaluate(0.0f); + } + } +} + +void control_map::handle_key_pressed(const event::key_pressed& event) +{ + for (const auto& [control, mapping]: key_mappings) + { + if (mapping.scancode == event.scancode && + (!mapping.keyboard || mapping.keyboard == event.keyboard)) + { + control->evaluate(1.0f); + } + } +} + +void control_map::handle_key_released(const event::key_released& event) +{ + for (const auto& [control, mapping]: key_mappings) + { + if (mapping.scancode == event.scancode && + (!mapping.keyboard || mapping.keyboard == event.keyboard)) + { + control->evaluate(0.0f); + } + } +} + +void control_map::handle_mouse_moved(const event::mouse_moved& event) +{ + for (const auto& [control, mapping]: mouse_motion_mappings) + { + if (!mapping.mouse || mapping.mouse == event.mouse) + { + const float difference = static_cast(event.difference[static_cast>(mapping.axis)]); + + if (difference && std::signbit(difference) == mapping.direction) + { + control->evaluate(std::abs(difference)); + control->evaluate(0.0f); + } + } + } +} + +void control_map::handle_mouse_scrolled(const event::mouse_scrolled& event) +{ + for (const auto& [control, mapping]: mouse_scroll_mappings) + { + if (!mapping.mouse || mapping.mouse == event.mouse) + { + const auto velocity = event.velocity[static_cast>(mapping.axis)]; + + if (velocity && std::signbit(velocity) == mapping.direction) + { + control->evaluate(std::abs(velocity)); + control->evaluate(0.0f); + } + } + } +} + +void control_map::handle_mouse_button_pressed(const event::mouse_button_pressed& event) +{ + for (const auto& [control, mapping]: mouse_button_mappings) + { + if (mapping.button == event.button && + (!mapping.mouse || mapping.mouse == event.mouse)) + { + control->evaluate(1.0f); + } + } +} + +void control_map::handle_mouse_button_released(const event::mouse_button_released& event) +{ + for (const auto& [control, mapping]: mouse_button_mappings) + { + if (mapping.button == event.button && + (!mapping.mouse || mapping.mouse == event.mouse)) + { + control->evaluate(0.0f); + } + } +} + +} // namespace input diff --git a/src/input/control-map.hpp b/src/input/control-map.hpp new file mode 100644 index 0000000..a887225 --- /dev/null +++ b/src/input/control-map.hpp @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEER_INPUT_CONTROL_MAP_HPP +#define ANTKEEER_INPUT_CONTROL_MAP_HPP + +#include "event/subscription.hpp" +#include "event/queue.hpp" +#include "input/control.hpp" +#include "input/event.hpp" +#include "input/mapping.hpp" +#include +#include +#include +#include + +namespace input { + +/** + * Maps input to a set of contextually-related controls. + */ +class control_map +{ +public: + /** + * Connects the input event signals of an event queue to the control map. + * + * @param queue Event queue to connect. + */ + void connect(::event::queue& queue); + + /** + * Disconnects all input event signals from the control map. + */ + void disconnect(); + + /** + * Maps input to a control. + * + * @param control Control to which input will be mapped. + * @param mapping Input mapping to add. + */ + /// @{ + void add_mapping(control& control, const mapping& mapping); + void add_mapping(control& control, gamepad_axis_mapping&& mapping); + void add_mapping(control& control, gamepad_button_mapping&& mapping); + void add_mapping(control& control, key_mapping&& mapping); + void add_mapping(control& control, mouse_button_mapping&& mapping); + void add_mapping(control& control, mouse_motion_mapping&& mapping); + void add_mapping(control& control, mouse_scroll_mapping&& mapping); + /// @} + + /** + * Unmaps input from a control. + * + * @param control Control from which input will be unmapped. + * @param type Type of input mapping to remove. + */ + void remove_mappings(control& control, mapping_type type); + + /** + * Unmaps all input from a control. + * + * @param control Control from which input will be unmapped. + */ + void remove_mappings(control& control); + + /** + * Unmaps all input from all controls in the control map. + */ + void remove_mappings(); + +private: + void handle_gamepad_axis_moved(const event::gamepad_axis_moved& event); + void handle_gamepad_button_pressed(const event::gamepad_button_pressed& event); + void handle_gamepad_button_released(const event::gamepad_button_released& event); + void handle_key_pressed(const event::key_pressed& event); + void handle_key_released(const event::key_released& event); + void handle_mouse_button_pressed(const event::mouse_button_pressed& event); + void handle_mouse_button_released(const event::mouse_button_released& event); + void handle_mouse_moved(const event::mouse_moved& event); + void handle_mouse_scrolled(const event::mouse_scrolled& event); + + std::vector> subscriptions; + std::vector> gamepad_axis_mappings; + std::vector> gamepad_button_mappings; + std::vector> key_mappings; + std::vector> mouse_button_mappings; + std::vector> mouse_motion_mappings; + std::vector> mouse_scroll_mappings; +}; + +} // namespace input + +#endif // ANTKEEER_INPUT_CONTROL_MAP_HPP diff --git a/src/input/control-set.hpp b/src/input/control-set.hpp deleted file mode 100644 index 831c370..0000000 --- a/src/input/control-set.hpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_INPUT_CONTROL_SET_HPP -#define ANTKEEPER_INPUT_CONTROL_SET_HPP - -#include - -namespace input { - -class control; - -/** - * A set of controls which can be managed simultaneously. - */ -class control_set -{ -public: - /** - * Adds a control to the control set. - * - * @param control Pointer to the control to add. - */ - void add_control(control* control); - - /** - * Removes a control from the control set. - * - * @param control Pointer to the control to remove. - */ - void remove_control(control* control); - - /** - * Removes all controls from the control set. - */ - void remove_controls(); - - /** - * Calls control::update() on each control in this control set. - */ - void update(); - - /** - * Enables or disables callbacks for all controls in the control set. - * - * @param enabled Whether to enable or disable callbacks for all controls in the control set. - */ - void set_callbacks_enabled(bool enabled); - - /** - * Returns the list of controls in the control set. - */ - const std::list* get_controls() const; - -private: - std::list controls; -}; - -inline const std::list* control_set::get_controls() const -{ - return &controls; -} - -} // namespace input - -#endif // ANTKEEPER_INPUT_CONTROL_SET_HPP - diff --git a/src/input/control.cpp b/src/input/control.cpp index 3c9fc0c..cf48b9d 100644 --- a/src/input/control.cpp +++ b/src/input/control.cpp @@ -17,109 +17,54 @@ * along with Antkeeper source code. If not, see . */ -#include "control.hpp" +#include "input/control.hpp" namespace input { +static bool default_threshold_function(float x) noexcept +{ + return x > 0.0f; +} + control::control(): - activation_threshold(0.0f), - current_value(0.0f), - previous_value(0.0f), - reset(false), - activated_callback(nullptr), - deactivated_callback(nullptr), - value_changed_callback(nullptr), - active_callback(nullptr), - callbacks_enabled(true) + threshold_function(default_threshold_function), + active(false), + activated_event{this}, + active_event{this, 0.0f}, + deactivated_event{this} {} -control::~control() -{} +void control::set_threshold_function(const threshold_function_type& function) +{ + threshold_function = function; +} -void control::update() +void control::evaluate(float value) { - // Perform callbacks, if enabled - if (callbacks_enabled) + // Store activation state + const bool was_active = active; + + // Re-evaluate activation state + active = threshold_function(value); + + // Emit events + if (active) { - if (activated_callback) - { - if (is_active() && !was_active()) - { - activated_callback(); - } - } - - if (deactivated_callback) - { - if (!is_active() && was_active()) - { - deactivated_callback(); - } - } - - if (value_changed_callback) + if (!was_active) { - if (current_value != previous_value) - { - if (is_active() || was_active()) - { - value_changed_callback(current_value); - } - } + activated_publisher.publish(activated_event); } - if (active_callback && is_active()) - { - active_callback(current_value); - } + active_event.input_value = value; + active_publisher.publish(active_event); } - - // Update previous value - previous_value = current_value; - - // Reset temporary values - if (reset) + else { - current_value = 0.0f; - reset = false; + if (was_active) + { + deactivated_publisher.publish(deactivated_event); + } } } -void control::set_current_value(float value) -{ - current_value = value; - reset = false; -} - -void control::set_temporary_value(float value) -{ - current_value = value; - reset = true; -} - -void control::set_activation_threshold(float threshold) -{ - activation_threshold = threshold; -} - -void control::set_activated_callback(std::function callback) -{ - this->activated_callback = callback; -} - -void control::set_deactivated_callback(std::function callback) -{ - this->deactivated_callback = callback; -} - -void control::set_value_changed_callback(std::function callback) -{ - this->value_changed_callback = callback; -} - -void control::set_active_callback(std::function callback) -{ - this->active_callback = callback; -} - } // namespace input diff --git a/src/input/control.hpp b/src/input/control.hpp index b2ce711..fa9cf68 100644 --- a/src/input/control.hpp +++ b/src/input/control.hpp @@ -20,123 +20,85 @@ #ifndef ANTKEEPER_INPUT_CONTROL_HPP #define ANTKEEPER_INPUT_CONTROL_HPP +#include "input/event.hpp" +#include "event/publisher.hpp" #include namespace input { /** - * A control can be bound to multiple types of input events. + * Generates control-related input events on activation state changes. */ class control { public: - /// Creates a control. - control(); - - /// Destroys a control. - virtual ~control(); - - /** - * Performs callbacks then sets the previous value equal to the current value. - */ - void update(); - /** - * Sets the current value of the control. + * Threshold function type. * - * @param value control value. + * Given an input value, returns `true` if the control should be considered active, and `false` otherwise. */ - void set_current_value(float value); - - /** - * This works the same as setting the current value, but causes the value to be reset on the next call to update. - */ - void set_temporary_value(float value); - + typedef std::function threshold_function_type; + + /// Constructs a control. + control(); + /** - * Sets the activation threshold. If the current value of the control is not greater than the activation threshold, the control will not be considered active. + * Sets the threshold function. * - * @param threshold Activation threshold. + * @param function Threshold function. */ - void set_activation_threshold(float threshold); - - /// Sets the callback for when the control is activated. - void set_activated_callback(std::function callback); - - /// Sets the callback for when the control is deactivated. - void set_deactivated_callback(std::function callback); - - /// Sets the callback for when the control value is changed. - void set_value_changed_callback(std::function callback); + void set_threshold_function(const threshold_function_type& function); - /// Sets the callback for while the control is active. - void set_active_callback(std::function callback); - /** - * Enables or disables callbacks. + * Evaluates the activation state of the control, according to its threshold function and an input value. * - * @param enabled Whether to enable or disable callbacks. + * @param value Input value. */ - void set_callbacks_enabled(bool enabled); - - /// Returns the activation threshold. The default value is 0.0. - float get_activation_threshold() const; - - /// Returns the current value of the control. - float get_current_value() const; - - /// Returns the previous value of the control. - float get_previous_value() const; - - /// Returns true if the control is currently active. - bool is_active() const; - - /// Returns true if the control was previously active when update() was last called. - bool was_active() const; + void evaluate(float value); + + /// Returns the threshold function. + [[nodiscard]] inline const threshold_function_type& get_threshold_function() const noexcept + { + return threshold_function; + } + + /// Returns `true` if the control is active, `false` otherwise. + [[nodiscard]] inline bool is_active() const noexcept + { + return active; + } + + /// Returns the channel through which control activated events are published. + [[nodiscard]] inline ::event::channel& get_activated_channel() noexcept + { + return activated_publisher.channel(); + } + + /// Returns the channel through which control active events are published. + [[nodiscard]] inline ::event::channel& get_active_channel() noexcept + { + return active_publisher.channel(); + } + + /// Returns the channel through which control deactivated events are published. + [[nodiscard]] inline ::event::channel& get_deactivated_channel() noexcept + { + return deactivated_publisher.channel(); + } private: - float activation_threshold; - float current_value; - float previous_value; - bool reset; - std::function activated_callback; - std::function deactivated_callback; - std::function value_changed_callback; - std::function active_callback; - bool callbacks_enabled; + threshold_function_type threshold_function; + bool active; + + event::control_activated activated_event; + event::control_active active_event; + event::control_deactivated deactivated_event; + + ::event::publisher activated_publisher; + ::event::publisher active_publisher; + ::event::publisher deactivated_publisher; }; -inline void control::set_callbacks_enabled(bool enabled) -{ - this->callbacks_enabled = enabled; -} - -inline float control::get_activation_threshold() const -{ - return activation_threshold; -} - -inline float control::get_current_value() const -{ - return current_value; -} - -inline float control::get_previous_value() const -{ - return previous_value; -} - -inline bool control::is_active() const -{ - return current_value > activation_threshold; -} - -inline bool control::was_active() const -{ - return previous_value > activation_threshold; -} - } // namespace input #endif // ANTKEEPER_INPUT_CONTROL_HPP - diff --git a/src/input/device-manager.cpp b/src/input/device-manager.cpp new file mode 100644 index 0000000..27a2553 --- /dev/null +++ b/src/input/device-manager.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "input/device-manager.hpp" + +namespace input { + +void device_manager::register_device(device& device) +{ + subscriptions.emplace(&device, device.get_connected_channel().subscribe(event_queue)); + subscriptions.emplace(&device, device.get_disconnected_channel().subscribe(event_queue)); + + switch (device.get_device_type()) + { + case device_type::gamepad: + register_gamepad(static_cast(device)); + break; + + case device_type::keyboard: + register_keyboard(static_cast(device)); + break; + + case device_type::mouse: + register_mouse(static_cast(device)); + break; + + default: + //std::unreachable(); + break; + } +} + +void device_manager::unregister_device(device& device) +{ + subscriptions.erase(&device); + + switch (device.get_device_type()) + { + case device_type::gamepad: + unregister_gamepad(static_cast(device)); + break; + + case device_type::keyboard: + unregister_keyboard(static_cast(device)); + break; + + case device_type::mouse: + unregister_mouse(static_cast(device)); + break; + + default: + //std::unreachable(); + break; + } +} + +void device_manager::register_gamepad(gamepad& gamepad) +{ + // Connect gamepad event signals to the event queue + subscriptions.emplace(&gamepad, gamepad.get_axis_moved_channel().subscribe(event_queue)); + subscriptions.emplace(&gamepad, gamepad.get_button_pressed_channel().subscribe(event_queue)); + subscriptions.emplace(&gamepad, gamepad.get_button_released_channel().subscribe(event_queue)); + + // Add gamepad to list of gamepads + gamepads.emplace(&gamepad); +} + +void device_manager::register_keyboard(keyboard& keyboard) +{ + // Connect keyboard event signals to the event queue + subscriptions.emplace(&keyboard, keyboard.get_key_pressed_channel().subscribe(event_queue)); + subscriptions.emplace(&keyboard, keyboard.get_key_released_channel().subscribe(event_queue)); + + // Add keyboard to list of keyboards + keyboards.emplace(&keyboard); +} + +void device_manager::register_mouse(mouse& mouse) +{ + // Connect mouse event signals to the event queue + subscriptions.emplace(&mouse, mouse.get_button_pressed_channel().subscribe(event_queue)); + subscriptions.emplace(&mouse, mouse.get_button_released_channel().subscribe(event_queue)); + subscriptions.emplace(&mouse, mouse.get_moved_channel().subscribe(event_queue)); + subscriptions.emplace(&mouse, mouse.get_scrolled_channel().subscribe(event_queue)); + + // Add mouse to list of mice + mice.emplace(&mouse); +} + +void device_manager::unregister_gamepad(gamepad& gamepad) +{ + gamepads.erase(&gamepad); +} + +void device_manager::unregister_keyboard(keyboard& keyboard) +{ + keyboards.erase(&keyboard); +} + +void device_manager::unregister_mouse(mouse& mouse) +{ + mice.erase(&mouse); +} + +} // namespace input diff --git a/src/input/device-manager.hpp b/src/input/device-manager.hpp new file mode 100644 index 0000000..65ec939 --- /dev/null +++ b/src/input/device-manager.hpp @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_INPUT_DEVICE_MANAGER_HPP +#define ANTKEEPER_INPUT_DEVICE_MANAGER_HPP + +#include "input/device.hpp" +#include "input/gamepad.hpp" +#include "input/keyboard.hpp" +#include "input/mouse.hpp" +#include "event/queue.hpp" +#include +#include +#include + +namespace input { + +/** + * Manages virtual input devices. + */ +class device_manager +{ +public: + /** + * Registers an input device. + * + * @param device Input device to register. + */ + void register_device(device& device); + + /** + * Unregisters an input device. + * + * @param device Input device to unregister. + */ + void unregister_device(device& device); + + /** + * Returns the event queue associated with registered input devices. + */ + [[nodiscard]] inline ::event::queue& get_event_queue() noexcept + { + return event_queue; + } + + /// Returns the set of registered gamepads. + [[nodiscard]] inline const std::unordered_set& get_gamepads() noexcept + { + return gamepads; + } + + /// Returns the set of registered keyboards. + [[nodiscard]] inline const std::unordered_set& get_keyboards() noexcept + { + return keyboards; + } + + /// Returns the set of registered mice. + [[nodiscard]] inline const std::unordered_set& get_mice() noexcept + { + return mice; + } + +private: + void register_gamepad(gamepad& gamepad); + void register_keyboard(keyboard& keyboard); + void register_mouse(mouse& mouse); + void unregister_gamepad(gamepad& gamepad); + void unregister_keyboard(keyboard& keyboard); + void unregister_mouse(mouse& mouse); + + ::event::queue event_queue; + std::multimap> subscriptions; + std::unordered_set gamepads; + std::unordered_set keyboards; + std::unordered_set mice; +}; + +} // namespace input + +#endif // ANTKEEPER_INPUT_DEVICE_MANAGER_HPP diff --git a/src/input/device-type.hpp b/src/input/device-type.hpp new file mode 100644 index 0000000..b7bc41b --- /dev/null +++ b/src/input/device-type.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_INPUT_DEVICE_TYPE_HPP +#define ANTKEEPER_INPUT_DEVICE_TYPE_HPP + +namespace input { + +/// Input device types. +enum class device_type +{ + /// Gamepad input device. + gamepad, + + /// Keyboard input device. + keyboard, + + /// Mouse input device. + mouse +}; + +} // namespace input + +#endif // ANTKEEPER_INPUT_DEVICE_TYPE_HPP diff --git a/src/input/device.cpp b/src/input/device.cpp index 17753be..ca98295 100644 --- a/src/input/device.cpp +++ b/src/input/device.cpp @@ -17,22 +17,29 @@ * along with Antkeeper source code. If not, see . */ -#include "device.hpp" +#include "input/device.hpp" namespace input { device::device(): - event_dispatcher(nullptr) + connected(true) {} -void device::set_event_dispatcher(::event_dispatcher* event_dispatcher) +void device::connect() { - this->event_dispatcher = event_dispatcher; + connected = true; + connected_publisher.publish({this}); } -void device::set_guid(const std::string& guid) +void device::disconnect() { - this->guid = guid; + connected = false; + disconnected_publisher.publish({this}); +} + +void device::set_uuid(const ::uuid& id) +{ + uuid = id; } } // namespace input diff --git a/src/input/device.hpp b/src/input/device.hpp index 20099df..a12e6d5 100644 --- a/src/input/device.hpp +++ b/src/input/device.hpp @@ -20,57 +20,81 @@ #ifndef ANTKEEPER_INPUT_DEVICE_HPP #define ANTKEEPER_INPUT_DEVICE_HPP -#include "event/event-dispatcher.hpp" +#include "input/device-type.hpp" +#include "input/event.hpp" +#include "event/publisher.hpp" +#include "utility/uuid.hpp" +#include #include namespace input { /** - * Base class for virtual devices which generate input events. + * Abstract base class for virtual devices that generate input events. */ class device { public: + /// Constructs an input device. device(); + + /// Destructs an input device. virtual ~device() = default; - - void set_event_dispatcher(event_dispatcher* event_dispatcher); - const event_dispatcher* get_event_dispatcher() const; - event_dispatcher* get_event_dispatcher(); /** - * Sets the globally unique identifier (GUID) of this input device. + * Simulates the device being connected. + */ + void connect(); + + /** + * Simulates the device being disconnected. * - * @param guid GUID string. + * @note Disconnected devices can still generate input events. */ - void set_guid(const std::string& guid); + void disconnect(); - /// Returns the globally unique identifier (GUID) of this input device. - const std::string& get_guid() const; - -protected: - event_dispatcher* event_dispatcher; + /// Returns `true` if the device is currently connected. + [[nodiscard]] inline bool is_connected() const noexcept + { + return connected; + } + + /** + * Sets the universally unique identifier (UUID) of this input device. + * + * @param id UUID. + */ + void set_uuid(const ::uuid& id); + + /// Returns the universally unique identifier (UUID) of this input device. + [[nodiscard]] inline const ::uuid& get_uuid() const noexcept + { + return uuid; + } + + /// Returns the channel through which device connected events are published. + [[nodiscard]] inline ::event::channel& get_connected_channel() noexcept + { + return connected_publisher.channel(); + } + + /// Returns the channel through which device disconnected events are published. + [[nodiscard]] inline ::event::channel& get_disconnected_channel() noexcept + { + return disconnected_publisher.channel(); + } + + /// Returns the input device type. + [[nodiscard]] virtual constexpr device_type get_device_type() const noexcept = 0; private: - std::string guid; + ::uuid uuid; + bool connected; + + ::event::publisher connected_publisher; + ::event::publisher disconnected_publisher; }; -inline const event_dispatcher* device::get_event_dispatcher() const -{ - return event_dispatcher; -} - -inline event_dispatcher* device::get_event_dispatcher() -{ - return event_dispatcher; -} - -inline const std::string& device::get_guid() const -{ - return guid; -} - } // namespace input #endif // ANTKEEPER_INPUT_DEVICE_HPP - diff --git a/src/input/event-router.cpp b/src/input/event-router.cpp deleted file mode 100644 index 5da99ab..0000000 --- a/src/input/event-router.cpp +++ /dev/null @@ -1,399 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "event-router.hpp" -#include "control.hpp" -#include "mapping.hpp" -#include "mouse.hpp" -#include "event/event-dispatcher.hpp" - -namespace input { - -event_router::event_router(): - event_dispatcher(nullptr) -{} - -event_router::~event_router() -{ - remove_mappings(); - set_event_dispatcher(nullptr); -} - -void event_router::add_mapping(const mapping& mapping) -{ - control* control = mapping.control; - - switch (mapping.get_type()) - { - case mapping_type::key: - { - input::key_mapping* key_mapping = new input::key_mapping(static_cast(mapping)); - key_mappings.push_back(key_mapping); - controls[control].push_back(key_mapping); - - break; - } - - case mapping_type::mouse_motion: - { - input::mouse_motion_mapping* mouse_motion_mapping = new input::mouse_motion_mapping(static_cast(mapping)); - mouse_motion_mappings.push_back(mouse_motion_mapping); - controls[control].push_back(mouse_motion_mapping); - - break; - } - - case mapping_type::mouse_wheel: - { - input::mouse_wheel_mapping* mouse_wheel_mapping = new input::mouse_wheel_mapping(static_cast(mapping)); - mouse_wheel_mappings.push_back(mouse_wheel_mapping); - controls[control].push_back(mouse_wheel_mapping); - - break; - } - - case mapping_type::mouse_button: - { - input::mouse_button_mapping* mouse_button_mapping = new input::mouse_button_mapping(static_cast(mapping)); - mouse_button_mappings.push_back(mouse_button_mapping); - controls[control].push_back(mouse_button_mapping); - - break; - } - - case mapping_type::gamepad_axis: - { - input::gamepad_axis_mapping* gamepad_axis_mapping = new input::gamepad_axis_mapping(static_cast(mapping)); - gamepad_axis_mappings.push_back(gamepad_axis_mapping); - controls[control].push_back(gamepad_axis_mapping); - - break; - } - - case mapping_type::gamepad_button: - { - input::gamepad_button_mapping* gamepad_button_mapping = new input::gamepad_button_mapping(static_cast(mapping)); - gamepad_button_mappings.push_back(gamepad_button_mapping); - controls[control].push_back(gamepad_button_mapping); - - break; - } - - default: - break; - } -} - -void event_router::remove_mappings(control* control) -{ - auto it = controls.find(control); - if (it != controls.end()) - { - for (mapping* mapping: it->second) - { - switch (mapping->get_type()) - { - case mapping_type::key: - key_mappings.remove(static_cast(mapping)); - break; - - case mapping_type::mouse_motion: - mouse_motion_mappings.remove(static_cast(mapping)); - break; - - case mapping_type::mouse_wheel: - mouse_wheel_mappings.remove(static_cast(mapping)); - break; - - case mapping_type::mouse_button: - mouse_button_mappings.remove(static_cast(mapping)); - break; - - case mapping_type::gamepad_axis: - gamepad_axis_mappings.remove(static_cast(mapping)); - break; - - case mapping_type::gamepad_button: - gamepad_button_mappings.remove(static_cast(mapping)); - break; - - default: - break; - } - - delete mapping; - } - - controls.erase(it); - } -} - -void event_router::remove_mappings(control* control, mapping_type type) -{ - auto it = controls.find(control); - if (it != controls.end()) - { - std::list flagged_mappings; - - for (mapping* mapping: it->second) - { - if (mapping->get_type() != type) - continue; - - // Flag mapping for deletion - flagged_mappings.push_back(mapping); - - switch (mapping->get_type()) - { - case mapping_type::key: - key_mappings.remove(static_cast(mapping)); - break; - - case mapping_type::mouse_motion: - mouse_motion_mappings.remove(static_cast(mapping)); - break; - - case mapping_type::mouse_wheel: - mouse_wheel_mappings.remove(static_cast(mapping)); - break; - - case mapping_type::mouse_button: - mouse_button_mappings.remove(static_cast(mapping)); - break; - - case mapping_type::gamepad_axis: - gamepad_axis_mappings.remove(static_cast(mapping)); - break; - - case mapping_type::gamepad_button: - gamepad_button_mappings.remove(static_cast(mapping)); - break; - - default: - break; - } - } - - // Delete flagged mappings - for (mapping* mapping: flagged_mappings) - { - it->second.remove(mapping); - delete mapping; - } - } -} - -void event_router::set_event_dispatcher(::event_dispatcher* event_dispatcher) -{ - if (this->event_dispatcher) - { - this->event_dispatcher->unsubscribe(this); - this->event_dispatcher->unsubscribe(this); - this->event_dispatcher->unsubscribe(this); - this->event_dispatcher->unsubscribe(this); - this->event_dispatcher->unsubscribe(this); - this->event_dispatcher->unsubscribe(this); - this->event_dispatcher->unsubscribe(this); - this->event_dispatcher->unsubscribe(this); - this->event_dispatcher->unsubscribe(this); - } - - this->event_dispatcher = event_dispatcher; - - if (event_dispatcher) - { - event_dispatcher->subscribe(this); - event_dispatcher->subscribe(this); - event_dispatcher->subscribe(this); - event_dispatcher->subscribe(this); - event_dispatcher->subscribe(this); - event_dispatcher->subscribe(this); - event_dispatcher->subscribe(this); - event_dispatcher->subscribe(this); - event_dispatcher->subscribe(this); - } -} - -void event_router::remove_mappings() -{ - for (auto it = controls.begin(); it != controls.end(); ++it) - { - for (mapping* mapping: it->second) - { - delete mapping; - } - } - - controls.clear(); - key_mappings.clear(); - mouse_motion_mappings.clear(); - mouse_wheel_mappings.clear(); - mouse_button_mappings.clear(); - gamepad_axis_mappings.clear(); - gamepad_button_mappings.clear(); -} - -const std::list* event_router::get_mappings(control* control) const -{ - auto it = controls.find(control); - if (it == controls.end()) - { - return nullptr; - } - - return &it->second; -} - -void event_router::handle_event(const key_pressed_event& event) -{ - for (const key_mapping* mapping: key_mappings) - { - if ((!mapping->keyboard || mapping->keyboard == event.keyboard) && mapping->scancode == event.scancode) - { - mapping->control->set_current_value(1.0f); - } - } -} - -void event_router::handle_event(const key_released_event& event) -{ - for (const key_mapping* mapping: key_mappings) - { - if ((!mapping->keyboard || mapping->keyboard == event.keyboard) && mapping->scancode == event.scancode) - { - mapping->control->set_current_value(0.0f); - } - } -} - -void event_router::handle_event(const mouse_moved_event& event) -{ - for (const mouse_motion_mapping* mapping: mouse_motion_mappings) - { - if (!mapping->mouse || mapping->mouse == event.mouse) - { - if (mapping->axis == mouse_motion_axis::negative_x && event.dx < 0) - { - mapping->control->set_temporary_value(mapping->control->get_current_value() - event.dx); - } - else if (mapping->axis == mouse_motion_axis::positive_x && event.dx > 0) - { - mapping->control->set_temporary_value(mapping->control->get_current_value() + event.dx); - } - else if (mapping->axis == mouse_motion_axis::negative_y && event.dy < 0) - { - mapping->control->set_temporary_value(mapping->control->get_current_value() - event.dy); - } - else if (mapping->axis == mouse_motion_axis::positive_y && event.dy > 0) - { - mapping->control->set_temporary_value(mapping->control->get_current_value() + event.dy); - } - } - } -} - -void event_router::handle_event(const mouse_wheel_scrolled_event& event) -{ - for (const mouse_wheel_mapping* mapping: mouse_wheel_mappings) - { - if (!mapping->mouse || mapping->mouse == event.mouse) - { - if (mapping->axis == mouse_wheel_axis::negative_x && event.x < 0) - { - mapping->control->set_temporary_value(mapping->control->get_current_value() - event.x); - } - else if (mapping->axis == mouse_wheel_axis::positive_x && event.x > 0) - { - mapping->control->set_temporary_value(mapping->control->get_current_value() + event.x); - } - else if (mapping->axis == mouse_wheel_axis::negative_y && event.y < 0) - { - mapping->control->set_temporary_value(mapping->control->get_current_value() - event.y); - } - else if (mapping->axis == mouse_wheel_axis::positive_y && event.y > 0) - { - mapping->control->set_temporary_value(mapping->control->get_current_value() + event.y); - } - } - } -} - -void event_router::handle_event(const mouse_button_pressed_event& event) -{ - for (const mouse_button_mapping* mapping: mouse_button_mappings) - { - if ((!mapping->mouse || mapping->mouse == event.mouse) && mapping->button == event.button) - { - mapping->control->set_current_value(1.0f); - } - } -} - -void event_router::handle_event(const mouse_button_released_event& event) -{ - for (const mouse_button_mapping* mapping: mouse_button_mappings) - { - if ((!mapping->mouse || mapping->mouse == event.mouse) && mapping->button == event.button) - { - mapping->control->set_current_value(0.0f); - } - } -} - -void event_router::handle_event(const gamepad_axis_moved_event& event) -{ - for (const gamepad_axis_mapping* mapping: gamepad_axis_mappings) - { - if ((!mapping->controller || mapping->controller == event.controller) && mapping->axis == event.axis) - { - if (mapping->negative && event.value >= 0.0f || !mapping->negative && event.value <= 0.0f) - { - mapping->control->set_current_value(0.0f); - } - else - { - mapping->control->set_current_value(std::abs(event.value)); - } - } - } -} - -void event_router::handle_event(const gamepad_button_pressed_event& event) -{ - for (const gamepad_button_mapping* mapping: gamepad_button_mappings) - { - if ((!mapping->controller || mapping->controller == event.controller) && mapping->button == event.button) - { - mapping->control->set_current_value(1.0f); - } - } -} - -void event_router::handle_event(const gamepad_button_released_event& event) -{ - for (const gamepad_button_mapping* mapping: gamepad_button_mappings) - { - if ((!mapping->controller || mapping->controller == event.controller) && mapping->button == event.button) - { - mapping->control->set_current_value(0.0f); - } - } -} - -} // namespace input diff --git a/src/input/event-router.hpp b/src/input/event-router.hpp deleted file mode 100644 index a34d126..0000000 --- a/src/input/event-router.hpp +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEER_INPUT_EVENT_ROUTER_HPP -#define ANTKEEER_INPUT_EVENT_ROUTER_HPP - -#include "event/input-events.hpp" -#include "event/event-handler.hpp" -#include "event/event-dispatcher.hpp" -#include -#include - -namespace input { - -class control; -enum class mapping_type; -class mapping; -class key_mapping; -class mouse_motion_mapping; -class mouse_wheel_mapping; -class mouse_button_mapping; -class gamepad_axis_mapping; -class gamepad_button_mapping; -enum class mouse_motion_axis; -enum class mouse_wheel_axis; - -/** - * Uses input mappings to route input events to controls. - */ -class event_router: - public event_handler, - public event_handler, - public event_handler, - public event_handler, - public event_handler, - public event_handler, - public event_handler, - public event_handler, - public event_handler -{ -public: - /** - * Creates an input router and subscribes it to the input events of the specified event dispatcher. - */ - event_router(); - - /** - * Destroys an input router and unsubscribes it from input events. - */ - ~event_router(); - - /** - * Adds an input mapping to the router. - * - * @param mapping Input mapping to add. - */ - void add_mapping(const mapping& mapping); - - /** - * Removes all input mappings from the router that are associated with the specified control. - * - * @param control Control with which associated input mappings should be removed. - */ - void remove_mappings(control* control); - - /** - * Removes all input mappings of a given type from the router that are associated with the specified control. - * - * @param control Control with which associated input mappings should be removed. - * @param type Type of input mapping to be removed. - */ - void remove_mappings(control* control, mapping_type type); - - /** - * Removes all input mappings from the router. - */ - void remove_mappings(); - - /** - * Sets the event dispatcher to which this input event router will subscribe itself. - */ - void set_event_dispatcher(event_dispatcher* event_dispatcher); - - /// Returns a list of mappings for the specified control, or nullptr if the control is unmapped. - const std::list* get_mappings(control* control) const; - -private: - virtual void handle_event(const key_pressed_event& event); - virtual void handle_event(const key_released_event& event); - virtual void handle_event(const mouse_moved_event& event); - virtual void handle_event(const mouse_wheel_scrolled_event& event); - virtual void handle_event(const mouse_button_pressed_event& event); - virtual void handle_event(const mouse_button_released_event& event); - virtual void handle_event(const gamepad_axis_moved_event& event); - virtual void handle_event(const gamepad_button_pressed_event& event); - virtual void handle_event(const gamepad_button_released_event& event); - - event_dispatcher* event_dispatcher; - std::map> controls; - std::list key_mappings; - std::list mouse_motion_mappings; - std::list mouse_wheel_mappings; - std::list mouse_button_mappings; - std::list gamepad_axis_mappings; - std::list gamepad_button_mappings; -}; - -} // namespace input - -#endif // ANTKEEER_INPUT_EVENT_ROUTER_HPP - diff --git a/src/input/event.hpp b/src/input/event.hpp new file mode 100644 index 0000000..5b57135 --- /dev/null +++ b/src/input/event.hpp @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_INPUT_EVENT_HPP +#define ANTKEEPER_INPUT_EVENT_HPP + +#include "input/gamepad-axis.hpp" +#include "input/gamepad-button.hpp" +#include "input/mouse-motion-axis.hpp" +#include "input/mouse-scroll-axis.hpp" +#include "input/scancode.hpp" +#include "input/mapping.hpp" +#include "input/modifier-key.hpp" +#include "math/vector.hpp" +#include +#include + +namespace input { + +class control; +class device; +class gamepad; +class keyboard; +class mouse; + +/** + * Input events. + */ +namespace event { + +/** + * Event generated when a control has been activated. + */ +struct control_activated +{ + /// Control that was activated. + control* control; +}; + +/** + * Event generated while a control is active. + */ +struct control_active +{ + /// Active control. + control* control; + + /// Control input value. + float input_value; +}; + +/** + * Event generated when a control has been deactivated. + */ +struct control_deactivated +{ + /// Control that was deactivated. + control* control; +}; + +/** + * Event generated when an input mapping has been generated. + */ +struct input_mapped +{ + /// Input mapping that was generated. + std::shared_ptr mapping; +}; + +/** + * Event generated when an input device has been connected. + */ +struct device_connected +{ + /// Device that was connected. + device* device; +}; + +/** + * Event generated when an input device has been disconnected. + */ +struct device_disconnected +{ + /// Device that was disconnected. + device* device; +}; + +/** + * Event generated when a gamepad button has been pressed. + */ +struct gamepad_button_pressed +{ + /// Gamepad that generated the event. + gamepad* gamepad; + + /// Gamepad button being pressed. + gamepad_button button; +}; + +/** + * Event generated when a gamepad button has been released. + */ +struct gamepad_button_released +{ + /// Gamepad that generated the event. + gamepad* gamepad; + + /// Gamepad button being released. + gamepad_button button; +}; + +/** + * Event generated when a gamepad axis has been moved. + */ +struct gamepad_axis_moved +{ + /// Gamepad that generated the event. + gamepad* gamepad; + + /// Gamepad axis being moved. + gamepad_axis axis; + + /// Position of the gamepad axis, on `[-1, 1]`. + float position; +}; + +/** + * Event generated when a keyboard key has been pressed. + */ +struct key_pressed +{ + /// Keyboard that generated the event. + keyboard* keyboard; + + /// Scancode of the key being pressed. + scancode scancode; + + /// `true` if the key press was generated by a key repeat, `false` otherwise. + bool repeat; + + /// Bit mask containing the active modifier keys. + std::uint16_t modifiers; +}; + +/** + * Event generated when a keyboard key has been released. + */ +struct key_released +{ + /// Keyboard that generated the event. + keyboard* keyboard; + + /// Scancode of the key being released. + scancode scancode; + + /// `true` if the key release was generated by a key repeat, `false` otherwise. + bool repeat; + + /// Bit mask containing the active modifier keys. + std::uint16_t modifiers; +}; + +/** + * Event generated when a mouse has been moved. + */ +struct mouse_moved +{ + /// Mouse that generated the event. + mouse* mouse; + + /// Mouse position, in pixels, relative to the window. + math::vector position; + + /// Relative movement of the mouse, in pixels. + math::vector difference; +}; + +/** + * Event generated when a mouse button has been pressed. + */ +struct mouse_button_pressed +{ + /// Mouse that generated the event. + mouse* mouse; + + /// Mouse position, in pixels, relative to the window, when the button was pressed. + math::vector position; + + /// Mouse button being pressed. + mouse_button button; +}; + +/** + * Event generated when a mouse button has been released. + */ +struct mouse_button_released +{ + /// Mouse that generated the event. + mouse* mouse; + + /// Mouse position, in pixels, relative to the window, when the button was released. + math::vector position; + + /// Mouse button being released. + mouse_button button; +}; + +/** + * Event generated when a mouse has been scrolled. + */ +struct mouse_scrolled +{ + /// Mouse that generated the event. + mouse* mouse; + + /// Mouse position, in pixels, relative to the window, when the mouse was scrolled. + math::vector position; + + /// Scroll velocity. + math::vector velocity; +}; + +/** + * Event generated when a window has been closed. + */ +struct window_closed {}; + +/** + * Event generated when a window has gained or lost focus. + */ +struct window_focus_changed +{ + /// `true` if the window is in focus, `false` otherwise. + bool in_focus; +}; + +/** + * Event generated when a window has been moved. + */ +struct window_moved +{ + /// Position of the window, in pixels. + math::vector position; +}; + +/** + * Event generated when a window has been resized. + */ +struct window_resized +{ + /// Window width, in pixels. + std::int32_t window_width; + + /// Window height, in pixels. + std::int32_t window_height; + + /// Viewport width, in pixels. + std::int32_t viewport_width; + + /// Viewport height, in pixels. + std::int32_t viewport_height; +}; + +} // namespace event +} // namespace input + +#endif // ANTKEEPER_INPUT_EVENT_HPP diff --git a/src/input/gamepad-axis.hpp b/src/input/gamepad-axis.hpp new file mode 100644 index 0000000..af0ecd8 --- /dev/null +++ b/src/input/gamepad-axis.hpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_INPUT_GAMEPAD_AXIS_HPP +#define ANTKEEPER_INPUT_GAMEPAD_AXIS_HPP + +#include + +namespace input { + +/// Gamepad axes. +enum class gamepad_axis: std::uint8_t +{ + /// Left stick X-axis. + left_stick_x, + + /// Left stick Y-axis. + left_stick_y, + + /// Right stick X-axis. + right_stick_x, + + /// Right stick Y-axis. + right_stick_y, + + /// Left trigger. + left_trigger, + + /// Right trigger. + right_trigger +}; + +} // namespace input + +#endif // ANTKEEPER_INPUT_GAMEPAD_AXIS_HPP diff --git a/src/input/gamepad-button.hpp b/src/input/gamepad-button.hpp new file mode 100644 index 0000000..72b58e5 --- /dev/null +++ b/src/input/gamepad-button.hpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_INPUT_GAMEPAD_BUTTON_HPP +#define ANTKEEPER_INPUT_GAMEPAD_BUTTON_HPP + +#include + +namespace input { + +/// Gamepad buttons. +enum class gamepad_button: std::uint8_t +{ + /// A button. + a, + + /// B button. + b, + + /// X button. + x, + + /// Y button. + y, + + /// Back button. + back, + + /// Guide button. + guide, + + /// Start button. + start, + + /// Left stick button. + left_stick, + + /// Right stick button. + right_stick, + + /// Left shoulder button. + left_shoulder, + + /// Right shoulder button. + right_shoulder, + + /// D-pad up button. + dpad_up, + + /// D-pad down button. + dpad_down, + + /// D-pad left button. + dpad_left, + + /// D-pad right button. + dpad_right +}; + +} // namespace input + +#endif // ANTKEEPER_INPUT_GAMEPAD_BUTTON_HPP diff --git a/src/input/gamepad.cpp b/src/input/gamepad.cpp index f47868b..00c3f26 100644 --- a/src/input/gamepad.cpp +++ b/src/input/gamepad.cpp @@ -17,17 +17,15 @@ * along with Antkeeper source code. If not, see . */ -#include "gamepad.hpp" -#include "event/input-events.hpp" -#include "event/event-dispatcher.hpp" +#include "input/gamepad.hpp" #include "math/map.hpp" #include +#include #include namespace input { gamepad::gamepad(): - connected(true), left_deadzone_cross(true), right_deadzone_cross(true), left_deadzone_roundness(0.0f), @@ -35,7 +33,7 @@ gamepad::gamepad(): { for (int i = 0; i < 6; ++i) { - axis_values[i] = 0.0f; + axis_positions[i] = 0.0f; axis_activation_min[i] = 0.0f; axis_activation_max[i] = 1.0f; axis_response_curves[i] = gamepad_response_curve::linear; @@ -44,13 +42,13 @@ gamepad::gamepad(): void gamepad::set_activation_threshold(gamepad_axis axis, float min, float max) { - axis_activation_min[static_cast(axis)] = min; - axis_activation_max[static_cast(axis)] = max; + axis_activation_min[static_cast>(axis)] = min; + axis_activation_max[static_cast>(axis)] = max; } void gamepad::set_response_curve(gamepad_axis axis, gamepad_response_curve curve) { - axis_response_curves[static_cast(axis)] = curve; + axis_response_curves[static_cast>(axis)] = curve; } void gamepad::set_left_deadzone_cross(bool cross) @@ -75,60 +73,45 @@ void gamepad::set_right_deadzone_roundness(float roundness) void gamepad::press(gamepad_button button) { - if (!device::event_dispatcher) - { - return; - } - - gamepad_button_pressed_event event; - event.controller = this; - event.button = button; - - device::event_dispatcher->queue(event); + button_pressed_publisher.publish({this, button}); } void gamepad::release(gamepad_button button) { - if (!device::event_dispatcher) - { - return; - } - - gamepad_button_released_event event; - event.controller = this; - event.button = button; - - device::event_dispatcher->queue(event); + button_released_publisher.publish({this, button}); } -void gamepad::move(gamepad_axis axis, float value) +void gamepad::move(gamepad_axis axis, float position) { - // Update axis value - axis_values[static_cast(axis)] = value; + const auto axis_index = static_cast>(axis); - if (!device::event_dispatcher) - { + /// @TODO Support arbitrary number of gamepad axes. + if (axis_index >= 6) return; - } + + // Update axis position + axis_positions[axis_index] = position; switch (axis) { - case gamepad_axis::left_x: - case gamepad_axis::left_y: + case gamepad_axis::left_stick_x: + case gamepad_axis::left_stick_y: if (left_deadzone_cross) handle_axial_motion(axis); else - handle_biaxial_motion(gamepad_axis::left_x, gamepad_axis::left_y); + handle_biaxial_motion(gamepad_axis::left_stick_x, gamepad_axis::left_stick_y); break; - - case gamepad_axis::right_x: - case gamepad_axis::right_y: + + case gamepad_axis::right_stick_x: + case gamepad_axis::right_stick_y: if (right_deadzone_cross) handle_axial_motion(axis); else - handle_biaxial_motion(gamepad_axis::right_x, gamepad_axis::right_y); + handle_biaxial_motion(gamepad_axis::right_stick_x, gamepad_axis::right_stick_y); break; + case gamepad_axis::left_trigger: + case gamepad_axis::right_trigger: default: handle_axial_motion(axis); break; @@ -137,69 +120,58 @@ void gamepad::move(gamepad_axis axis, float value) void gamepad::handle_axial_motion(gamepad_axis axis) { + const auto axis_index = static_cast>(axis); + // Get axis parameters - const int axis_index = static_cast(axis); const float activation_min = axis_activation_min[axis_index]; const float activation_max = axis_activation_max[axis_index]; - const float axis_value = axis_values[axis_index]; + const float axis_position = axis_positions[axis_index]; const gamepad_response_curve response_curve = axis_response_curves[axis_index]; - // Build event - gamepad_axis_moved_event event; - event.controller = this; - event.axis = axis; - - if (std::abs(axis_value) > activation_min) + // Remap axis position + float remapped_position = 0.0f; + if (std::abs(axis_position) > activation_min) { - // Remap response value according to activation thresholds and clamp to `[0, 1]`. - float response = math::map(std::abs(axis_value), activation_min, activation_max, 0.0f, 1.0f); + // Remap position according to activation thresholds and clamp to `[0, 1]`. + float response = math::map(std::abs(axis_position), activation_min, activation_max, 0.0f, 1.0f); response = std::clamp(response, 0.0f, 1.0f); - // Remap response value according to axis response curve + // Remap position according to axis response curve response = curve_response(axis, response); // Restore sign of axis motion - response = (axis_value < 0.0f) ? -response : response; + response = (axis_position < 0.0f) ? -response : response; - event.value = response; - } - else - { - event.value = 0.0f; + remapped_position = response; } - // Dispatch event - device::event_dispatcher->queue(event); + axis_moved_publisher.publish({this, axis, remapped_position}); } void gamepad::handle_biaxial_motion(gamepad_axis axis_x, gamepad_axis axis_y) { // Get axis parameters - const int x_axis_index = static_cast(axis_x); - const int y_axis_index = static_cast(axis_y); + const int x_axis_index = static_cast>(axis_x); + const int y_axis_index = static_cast>(axis_y); const float x_activation_min = axis_activation_min[x_axis_index]; const float x_activation_max = axis_activation_max[x_axis_index]; const float y_activation_min = axis_activation_min[y_axis_index]; const float y_activation_max = axis_activation_max[y_axis_index]; - const float x_axis_value = axis_values[x_axis_index]; - const float y_axis_value = axis_values[y_axis_index]; + const float x_axis_position = axis_positions[x_axis_index]; + const float y_axis_position = axis_positions[y_axis_index]; const gamepad_response_curve x_response_curve = axis_response_curves[x_axis_index]; const gamepad_response_curve y_response_curve = axis_response_curves[y_axis_index]; - const float deadzone_roundness = (axis_x == gamepad_axis::left_x) ? left_deadzone_roundness : right_deadzone_roundness; + const float deadzone_roundness = (axis_x == gamepad_axis::left_stick_x) ? left_deadzone_roundness : right_deadzone_roundness; const float radius = std::min(x_activation_min, y_activation_min) * deadzone_roundness; - const float dx = std::max(0.0f, std::abs(x_axis_value) - x_activation_min + radius); - const float dy = std::max(0.0f, std::abs(y_axis_value) - y_activation_min + radius); + const float dx = std::max(0.0f, std::abs(x_axis_position) - x_activation_min + radius); + const float dy = std::max(0.0f, std::abs(y_axis_position) - y_activation_min + radius); const float distance = std::sqrt(dx * dx + dy * dy) - radius; - // Build event - gamepad_axis_moved_event event; - event.controller = this; - if (distance > 0.0f) { - const float nx = std::abs(x_axis_value) / distance; - const float ny = std::abs(y_axis_value) / distance; + const float nx = std::abs(x_axis_position) / distance; + const float ny = std::abs(y_axis_position) / distance; const float ndx = (distance - x_activation_min) / (x_activation_max - x_activation_min); const float ndy = (distance - y_activation_min) / (y_activation_max - y_activation_min); @@ -210,29 +182,23 @@ void gamepad::handle_biaxial_motion(gamepad_axis axis_x, gamepad_axis axis_y) response_y = curve_response(axis_y, response_y); // Restore signs of axis motions - response_x = (x_axis_value < 0.0f) ? -response_x : response_x; - response_y = (y_axis_value < 0.0f) ? -response_y : response_y; + response_x = (x_axis_position < 0.0f) ? -response_x : response_x; + response_y = (y_axis_position < 0.0f) ? -response_y : response_y; - event.value = response_x; - event.axis = axis_x; - device::event_dispatcher->queue(event); - event.value = response_y; - event.axis = axis_y; - device::event_dispatcher->queue(event); + axis_moved_publisher.publish({this, axis_x, response_x}); + axis_moved_publisher.publish({this, axis_y, response_y}); } else { - event.value = 0.0f; - event.axis = axis_x; - device::event_dispatcher->queue(event); - event.axis = axis_y; - device::event_dispatcher->queue(event); + axis_moved_publisher.publish({this, axis_x, 0.0f}); + axis_moved_publisher.publish({this, axis_y, 0.0f}); } } float gamepad::curve_response(gamepad_axis axis, float response) const { - const gamepad_response_curve response_curve = axis_response_curves[static_cast(axis)]; + const auto axis_index = static_cast>(axis); + const gamepad_response_curve response_curve = axis_response_curves[axis_index]; switch (response_curve) { @@ -251,25 +217,4 @@ float gamepad::curve_response(gamepad_axis axis, float response) const return response; } -void gamepad::connect(bool reconnected) -{ - connected = true; - - gamepad_connected_event event; - event.controller = this; - event.reconnected = reconnected; - - device::event_dispatcher->queue(event); -} - -void gamepad::disconnect() -{ - connected = false; - - gamepad_disconnected_event event; - event.controller = this; - - device::event_dispatcher->queue(event); -} - } // namespace input diff --git a/src/input/gamepad.hpp b/src/input/gamepad.hpp index 008297a..d2b9538 100644 --- a/src/input/gamepad.hpp +++ b/src/input/gamepad.hpp @@ -20,52 +20,14 @@ #ifndef ANTKEEPER_INPUT_GAMEPAD_HPP #define ANTKEEPER_INPUT_GAMEPAD_HPP -#include "device.hpp" +#include "input/device.hpp" +#include "input/event.hpp" +#include "input/gamepad-axis.hpp" +#include "input/gamepad-button.hpp" +#include "event/publisher.hpp" namespace input { -/// Gamepad buttons. -enum class gamepad_button -{ - a, - b, - x, - y, - back, - guide, - start, - left_stick, - right_stick, - left_shoulder, - right_shoulder, - dpad_up, - dpad_down, - dpad_left, - dpad_right -}; - -/// Gamepad axes. -enum class gamepad_axis -{ - /// Left stick x-axis. - left_x, - - /// Left stick y-axis. - left_y, - - /// Right stick x-axis. - right_x, - - /// Right stick y-axis. - right_y, - - /// Left trigger. - left_trigger, - - /// Right trigger. - right_trigger, -}; - /// Gamepad axis activation response curves. enum class gamepad_response_curve { @@ -80,17 +42,17 @@ enum class gamepad_response_curve }; /** - * A virtual gamepad which can generate gamepad-related input events and pass them to an event dispatcher. + * A virtual gamepad which generates gamepad-related input events. */ class gamepad: public device { public: /** - * Creates a gamepad input device. + * Constructs a gamepad input device. */ gamepad(); - /// Destroys a gamepad input device. + /// Destructs a gamepad input device. virtual ~gamepad() = default; /** @@ -141,46 +103,55 @@ public: /** * Simulates a gamepad button press. * - * @param button Index of the pressed button. + * @param button Button to press. */ void press(gamepad_button button); /** * Simulates a gamepad button release. * - * @param button Index of the released button. + * @param button Button to release. */ void release(gamepad_button button); /** * Simulates a gamepad axis movement. * - * @param axis Index of the moved axis. - * @param negative Whether the movement was negative or positive. - * @param value Normalized degree of movement. - */ - void move(gamepad_axis axis, float value); - - /** - * Simulates a gamepad being connected. - * - * @param reconnected `true` if the controller is being reconnected, or `false` if the controller is being connected for the first time. + * @param axis Gamepad axis. + * @param position Position on the axis, on `[-1, 1]`. */ - void connect(bool reconnnected); - - /// Simulates a gamepad being disconnected. - void disconnect(); - - /// Returns `true` if the controller is currently connected. - bool is_connected() const; + void move(gamepad_axis axis, float position); + + /// Returns the channel through which gamepad button pressed events are published. + [[nodiscard]] inline ::event::channel& get_button_pressed_channel() noexcept + { + return button_pressed_publisher.channel(); + } + + /// Returns the channel through which gamepad button released events are published. + [[nodiscard]] inline ::event::channel& get_button_released_channel() noexcept + { + return button_released_publisher.channel(); + } + + /// Returns the channel through which gamepad axis moved events are published. + [[nodiscard]] inline ::event::channel& get_axis_moved_channel() noexcept + { + return axis_moved_publisher.channel(); + } + + /// Returns device_type::gamepad. + [[nodiscard]] inline virtual constexpr device_type get_device_type() const noexcept + { + return device_type::gamepad; + } private: void handle_axial_motion(gamepad_axis axis); void handle_biaxial_motion(gamepad_axis axis_x, gamepad_axis axis_y); float curve_response(gamepad_axis axis, float response) const; - bool connected; - float axis_values[6]; + float axis_positions[6]; float axis_activation_min[6]; float axis_activation_max[6]; gamepad_response_curve axis_response_curves[6]; @@ -189,13 +160,12 @@ private: bool right_deadzone_cross; float left_deadzone_roundness; float right_deadzone_roundness; + + ::event::publisher button_pressed_publisher; + ::event::publisher button_released_publisher; + ::event::publisher axis_moved_publisher; }; -inline bool gamepad::is_connected() const -{ - return connected; -} - } // namespace input #endif // ANTKEEPER_INPUT_GAMEPAD_HPP diff --git a/src/input/input.hpp b/src/input/input.hpp deleted file mode 100644 index 59956f0..0000000 --- a/src/input/input.hpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_INPUT_HPP -#define ANTKEEPER_INPUT_HPP - -/// Input devices and events. -namespace input {} - -#include "control.hpp" -#include "control-set.hpp" -#include "device.hpp" -#include "event-router.hpp" -#include "gamepad.hpp" -#include "keyboard.hpp" -#include "listener.hpp" -#include "mapper.hpp" -#include "mapping.hpp" -#include "mouse.hpp" -#include "scancode.hpp" - -#endif // ANTKEEPER_INPUT_HPP diff --git a/src/input/keyboard.cpp b/src/input/keyboard.cpp index 2a486e2..f981cd7 100644 --- a/src/input/keyboard.cpp +++ b/src/input/keyboard.cpp @@ -17,328 +17,19 @@ * along with Antkeeper source code. If not, see . */ -#include "keyboard.hpp" -#include "scancode.hpp" -#include "event/event-dispatcher.hpp" -#include "event/input-events.hpp" +#include "input/keyboard.hpp" +#include "input/scancode.hpp" namespace input { -const char* keyboard::get_scancode_name(scancode scancode) +void keyboard::press(scancode scancode, bool repeat, std::uint16_t modifiers) { - return scancode_names[static_cast(scancode)]; -}; - -scancode keyboard::get_scancode_from_name(const char* name) -{ - auto it = scancode_map.find(std::string(name)); - if (it == scancode_map.end()) - { - return scancode::unknown; - } - - return it->second; + key_pressed_publisher.publish({this, scancode, repeat, modifiers}); } -keyboard::keyboard() -{} - -keyboard::~keyboard() -{} - -void keyboard::press(scancode scancode) +void keyboard::release(scancode scancode, bool repeat, std::uint16_t modifiers) { - if (!device::event_dispatcher) - { - return; - } - - key_pressed_event event; - event.keyboard = this; - event.scancode = scancode; - - device::event_dispatcher->queue(event); + key_released_publisher.publish({this, scancode, repeat, modifiers}); } -void keyboard::release(scancode scancode) -{ - if (!device::event_dispatcher) - { - return; - } - - key_released_event event; - event.keyboard = this; - event.scancode = scancode; - - device::event_dispatcher->queue(event); -} - -std::map keyboard::build_scancode_map() -{ - std::map scancode_map; - for (std::size_t i = 0; i <= static_cast(scancode::audio_fast_forward); ++i) - { - if (scancode_names[i] != nullptr) - { - std::string scancode_name = scancode_names[i]; - scancode_map[scancode_name] = static_cast(i); - } - } - - return scancode_map; -} - -const char* keyboard::scancode_names[] = -{ - nullptr, // UNKNOWN - "A", // A - "B", // B - "C", // C - "D", // D - "E", // E - "F", // F - "G", // G - "H", // H - "I", // I - "J", // J - "K", // K - "L", // L - "M", // M - "N", // N - "O", // O - "P", // P - "Q", // Q - "R", // R - "S", // S - "T", // T - "U", // U - "V", // V - "W", // W - "X", // X - "Y", // Y - "Z", // Z - "1", // ONE - "2", // TWO - "3", // THREE - "4", // FOUR - "5", // FIVE - "6", // SIX - "7", // SEVEN - "8", // EIGHT - "9", // NINE - "0", // ZERO - "Enter", // ENTER - "Escape", // ESCAPE - "Backspace", // BACKSPACE - "Tab", // TAB - "Space", // SPACE - "-", // MINUS - "=", // EQUALS - "[", // LEFTBRACKET - "]", // RIGHTBRACKET - "\\", // BACKSLASH - "#", // NONUSHASH - ";", // SEMICOLON - "'", // APOSTROPHE - "`", // GRAVE - ",", // COMMA - ".", // PERIOD - "/", // SLASH - "Caps Lock", // CAPSLOCK - "F1", // F1 - "F2", // F2 - "F3", // F3 - "F4", // F4 - "F5", // F5 - "F6", // F6 - "F7", // F7 - "F8", // F8 - "F9", // F9 - "F10", // F10 - "F11", // F11 - "F12", // F12 - "Print Screen", // PRINTSCREEN - "Scroll Lock", // SCROLLLOCK - "Pause", // PAUSE - "Insert", // INSERT - "Home", // HOME - "Page Up", // PAGEUP - "Delete", // DELETE - "End", // END - "Page Down", // PAGEDOWN - "Right", // RIGHT - "Left", // LEFT - "Down", // DOWN - "Up", // UP - "Num Lock", // NUMLOCKCLEAR - "Keypad /", // KP_DIVIDE - "Keypad *", // KP_MULTIPLY - "Keypad -", // KP_MINUS - "Keypad +", // KP_PLUS - "Keypad Enter", // KP_ENTER - "Keypad 1", // KP_1 - "Keypad 2", // KP_2 - "Keypad 3", // KP_3 - "Keypad 4", // KP_4 - "Keypad 5", // KP_5 - "Keypad 6", // KP_6 - "Keypad 7", // KP_7 - "Keypad 8", // KP_8 - "Keypad 9", // KP_9 - "Keypad 0", // KP_0 - "Keypad .", // KP_PERIOD - nullptr, // NONUSBACKSLASH - "Application", // APPLICATION - "Power", // POWER - "Keypad =", // KP_EQUALS - "F13", // F13 - "F14", // F14 - "F15", // F15 - "F16", // F16 - "F17", // F17 - "F18", // F18 - "F19", // F19 - "F20", // F20 - "F21", // F21 - "F22", // F22 - "F23", // F23 - "F24", // F24 - "Execute", // EXECUTE - "Help", // HELP - "Menu", // MENU - "Select", // SELECT - "Stop", // STOP - "Again", // AGAIN - "Undo", // UNDO - "Cut", // CUT - "Copy", // COPY - "Paste", // PASTE - "Find", // FIND - "Mute", // MUTE - "Volume Up", // VOLUMEUP - "Volume Down", // VOLUMEDOWN - nullptr, // LOCKINGCAPSLOCK - nullptr, // LOCKINGNUMLOCK - nullptr, // LOCKINGSCROLLLOCK - "Keypad ,", // KP_COMMA - "Keypad = (AS400)", // KP_EQUALSAS400 - nullptr, // INTERNATIONAL1 - nullptr, // INTERNATIONAL2 - nullptr, // INTERNATIONAL3 - nullptr, // INTERNATIONAL4 - nullptr, // INTERNATIONAL5 - nullptr, // INTERNATIONAL6 - nullptr, // INTERNATIONAL7 - nullptr, // INTERNATIONAL8 - nullptr, // INTERNATIONAL9 - nullptr, // LANG1 - nullptr, // LANG2 - nullptr, // LANG3 - nullptr, // LANG4 - nullptr, // LANG5 - nullptr, // LANG6 - nullptr, // LANG7 - nullptr, // LANG8 - nullptr, // LANG9 - "Alt Erase", // ALTERASE - "Sys Req", // SYSREQ - "Cancel", // CANCEL - "Clear", // CLEAR - "Prior", // PRIOR - "Return", // RETURN2 - "Separator", // SEPARATOR - "Out", // OUT - "Oper", // OPER - "Clear/Again", // CLEARAGAIN - "CrSel", // CRSEL - "ExSel", // EXSEL - "Keypad 00", // KP_00 - "Keypad 000", // KP_000 - "Thousands Separator", // THOUSANDSSEPARATOR - "Decimal Separator", // DECIMALSEPARATOR - "Currency Unit", // CURRENCYUNIT - "Currency Sub-Unit", // CURRENCYSUBUNIT - "Keypad (", // KP_LEFTPAREN - "Keypad )", // KP_RIGHTPAREN - "Keypad {", // KP_LEFTBRACE - "Keypad }", // KP_RIGHTBRACE - "Keypad Tab", // KP_TAB - "Keypad Backspace", // KP_BACKSPACE - "Keypad A", // KP_A - "Keypad B", // KP_B - "Keypad C", // KP_C - "Keypad D", // KP_D - "Keypad E", // KP_E - "Keypad F", // KP_F - "Keypad XOR", // KP_XOR - "Keypad ^", // KP_POWER - "Keypad %", // KP_PERCENT - "Keypad <", // KP_LESS - "Keypad >", // KP_GREATER - "Keypad &", // KP_AMPERSAND - "Keypad &&", // KP_DBLAMPERSAND - "Keypad |", // KP_VERTICALBAR - "Keypad ||", // KP_DBLVERTICALBAR - "Keypad :", // KP_COLON - "Keypad #", // KP_HASH - "Keypad Space", // KP_SPACE - "Keypad @", // KP_AT - "Keypad !", // KP_EXCLAM - "Keypad Mem Store", // KP_MEMSTORE - "Keypad Mem Recall", // KP_MEMRECALL - "Keypad Mem Clear", // KP_MEMCLEAR - "Keypad Mem Add", // KP_MEMADD - "Keypad Mem Subtract", // KP_MEMSUBTRACT - "Keypad Mem Multiply", // KP_MEMMULTIPLY - "Keypad Mem Divide", // KP_MEMDIVIDE - "Keypad +/-", // KP_PLUSMINUS - "Keypad Clear", // KP_CLEAR - "Keypad Clear Entry", // KP_CLEARENTRY - "Keypad Binary", // KP_BINARY - "Keypad Octal", // KP_OCTAL - "Keypad Decimal", // KP_DECIMAL - "Keypad Hexadecimal", // KP_HEXADECIMAL - "Left Ctrl", // LCTRL - "Left Shift", // LSHIFT - "Left Alt", // LALT - "Left GUI", // LGUI - "Right Ctrl", // RCTRL - "Right Shift", // RSHIFT - "Right Alt", // RALT - "Right GUI", // RGUI - "Mode Switch", // MODE - "Audio Next", // AUDIONEXT - "Audio Prev", // AUDIOPREV - "Audio Stop", // AUDIOSTOP - "Audio Play", // AUDIOPLAY - "Audio Mute", // AUDIOMUTE - "Media Select", // MEDIASELECT - "WWW", // WWW - "Mail", // MAIL - "Calculator", // CALCULATOR - "Computer", // COMPUTER - "AC Search", // AC_SEARCH - "AC Home", // AC_HOME - "AC Back", // AC_BACK - "AC Forward", // AC_FORWARD - "AC Stop", // AC_STOP - "AC Refresh", // AC_REFRESH - "AC Bookmarks", // AC_BOOKMARKS - "Brightness Down", // BRIGHTNESSDOWN - "Brightness Up", // BRIGHTNESSUP - "Display Switch", // DISPLAYSWITCH - "KBD Illum Toggle", // KBDILLUMTOGGLE - "KBD Illum Down", // KBDILLUMDOWN - "KBD Illum Up", // KBDILLUMUP - "Eject", // EJECT - "Sleep", // SLEEP - "App 1", // APP1 - "App 2", // APP2 - "Audio Rewind", // AUDIOREWIND - "Audio Fast-Forward", // AUDIOFASTFORWARD -}; - -std::map keyboard::scancode_map = keyboard::build_scancode_map(); - } // namespace input diff --git a/src/input/keyboard.hpp b/src/input/keyboard.hpp index a161347..af45867 100644 --- a/src/input/keyboard.hpp +++ b/src/input/keyboard.hpp @@ -20,62 +20,69 @@ #ifndef ANTKEEPER_INPUT_KEYBOARD_HPP #define ANTKEEPER_INPUT_KEYBOARD_HPP -#include "device.hpp" -#include -#include +#include "input/device.hpp" +#include "input/event.hpp" +#include "input/scancode.hpp" +#include "input/modifier-key.hpp" +#include "event/publisher.hpp" namespace input { -enum class scancode; - /** - * A virtual keyboard which can generate keyboard-related input events and pass them to an event dispatcher. + * A virtual keyboard which generates keyboard-related input events. */ class keyboard: public device { public: /** - * Returns the UTF-8 encoded name of a scancode. - */ - static const char* get_scancode_name(scancode scancode); - - /** - * Returns the scancode corresponding to a scancode name. - * - * @param name Name of a scancode. - * @return Corresponding scancode, or nullptr if a matching scancode was not found. - */ - static scancode get_scancode_from_name(const char* name); - - /** - * Creates a keyboard input device. + * Constructs a keyboard input device. */ - keyboard(); - - /// Destroys a keyboard input device. - virtual ~keyboard(); + keyboard() = default; + + /// Destructs a keyboard input device. + virtual ~keyboard() = default; /** * Simulates a key press. * - * @param scancode scancode of the simulated key press. + * @param scancode Scancode of the key to press. + * @param repeat `true` if the key press is from a key repeat, `false` otherwise. + * @param modifiers Bit mask containing the active modifier keys. */ - void press(scancode scancode); - + void press(scancode scancode, bool repeat = false, std::uint16_t modifiers = modifier_key::none); + /** * Simulates a key release. * - * @param scancode scancode of the simulated key release. + * @param scancode Scancode of the key to release. + * @param repeat `true` if the key release is from a key repeat, `false` otherwise. + * @param modifiers Bit mask containing the active modifier keys. */ - void release(scancode scancode); + void release(scancode scancode, bool repeat = false, std::uint16_t modifiers = modifier_key::none); + + /// Returns the channel through which key pressed events are published. + [[nodiscard]] inline ::event::channel& get_key_pressed_channel() noexcept + { + return key_pressed_publisher.channel(); + } + + /// Returns the channel through which key released events are published. + [[nodiscard]] inline ::event::channel& get_key_released_channel() noexcept + { + return key_released_publisher.channel(); + } + + /// Returns device_type::keyboard. + [[nodiscard]] inline virtual constexpr device_type get_device_type() const noexcept + { + return device_type::keyboard; + } private: - static std::map build_scancode_map(); - static const char* scancode_names[]; - static std::map scancode_map; + ::event::publisher key_pressed_publisher; + ::event::publisher key_released_publisher; }; } // namespace input #endif // ANTKEEPER_INPUT_KEYBOARD_HPP - diff --git a/src/input/listener.cpp b/src/input/listener.cpp deleted file mode 100644 index 6d57814..0000000 --- a/src/input/listener.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "listener.hpp" -#include "event/event-dispatcher.hpp" -#include - -namespace input { - -listener::listener(): - event_dispatcher(nullptr), - callback(nullptr), - enabled(false) -{} - -listener::~listener() -{ - set_event_dispatcher(nullptr); -} - -void listener::set_event_dispatcher(::event_dispatcher* event_dispatcher) -{ - if (event_dispatcher != this->event_dispatcher) - { - if (this->event_dispatcher) - { - this->event_dispatcher->unsubscribe(this); - this->event_dispatcher->unsubscribe(this); - this->event_dispatcher->unsubscribe(this); - this->event_dispatcher->unsubscribe(this); - this->event_dispatcher->unsubscribe(this); - this->event_dispatcher->unsubscribe(this); - } - - if (event_dispatcher) - { - event_dispatcher->subscribe(this); - event_dispatcher->subscribe(this); - event_dispatcher->subscribe(this); - event_dispatcher->subscribe(this); - event_dispatcher->subscribe(this); - event_dispatcher->subscribe(this); - } - - this->event_dispatcher = event_dispatcher; - } -} - -void listener::set_callback(std::function callback) -{ - this->callback = callback; -} - -void listener::set_enabled(bool enabled) -{ - this->enabled = enabled; -} - -void listener::handle_event(const key_pressed_event& event) -{ - if (enabled && callback) - callback(event); -} - -void listener::handle_event(const mouse_moved_event& event) -{ - if (enabled && callback) - callback(event); -} - -void listener::handle_event(const mouse_button_pressed_event& event) -{ - if (enabled && callback) - callback(event); -} - -void listener::handle_event(const mouse_wheel_scrolled_event& event) -{ - if (enabled && callback) - callback(event); -} - -void listener::handle_event(const gamepad_button_pressed_event& event) -{ - if (enabled && callback) - callback(event); -} - -void listener::handle_event(const gamepad_axis_moved_event& event) -{ - if (enabled && callback) - callback(event); -} - -} // namespace input diff --git a/src/input/listener.hpp b/src/input/listener.hpp deleted file mode 100644 index 94a4ab5..0000000 --- a/src/input/listener.hpp +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_INPUT_LISTENER_HPP -#define ANTKEEPER_INPUT_LISTENER_HPP - -#include "event/input-events.hpp" -#include "event/event-handler.hpp" -#include "event/event-dispatcher.hpp" -#include - -namespace input { - -/** - * Listens for all types of input events. - */ -class listener: - public event_handler, - public event_handler, - public event_handler, - public event_handler, - public event_handler, - public event_handler -{ -public: - /** - * Creates an input listener. - */ - listener(); - - /** - * Destroys an input listener. - */ - virtual ~listener(); - - /** - * Sets the event dispatcher to which this input event router will subscribe itself. - */ - void set_event_dispatcher(event_dispatcher* event_dispatcher); - - /** - * Sets the input event callback function. - * - * @param callback Callback function which operates on an input event. - */ - void set_callback(std::function event); - - /** - * Enables or disables the input listening. - * - * @param enabled Whether to enable input listening or not. - */ - void set_enabled(bool enabled); - - /** - * Returns true if input listening is enabled. - */ - bool is_enabled() const; - -private: - void handle_event(const key_pressed_event& event); - void handle_event(const mouse_moved_event& event); - void handle_event(const mouse_wheel_scrolled_event& event); - void handle_event(const mouse_button_pressed_event& event); - void handle_event(const gamepad_axis_moved_event& event); - void handle_event(const gamepad_button_pressed_event& event); - - event_dispatcher* event_dispatcher; - std::function callback; - bool enabled; -}; - -inline bool listener::is_enabled() const -{ - return enabled; -} - -} // namespace input - -#endif // ANTKEEPER_INPUT_LISTENER_HPP - diff --git a/src/input/mapper.cpp b/src/input/mapper.cpp index 5973362..8f3941d 100644 --- a/src/input/mapper.cpp +++ b/src/input/mapper.cpp @@ -17,142 +17,70 @@ * along with Antkeeper source code. If not, see . */ -#include "mapper.hpp" -#include "mouse.hpp" -#include "event/event-dispatcher.hpp" +#include "input/mapper.hpp" +#include namespace input { -mapper::mapper(): - event_dispatcher(nullptr), - control(nullptr), - callback(nullptr), - enabled(false) -{} - -mapper::~mapper() -{ - set_event_dispatcher(nullptr); -} - -void mapper::set_event_dispatcher(::event_dispatcher* event_dispatcher) -{ - if (this->event_dispatcher) - { - this->event_dispatcher->unsubscribe(this); - this->event_dispatcher->unsubscribe(this); - this->event_dispatcher->unsubscribe(this); - this->event_dispatcher->unsubscribe(this); - this->event_dispatcher->unsubscribe(this); - this->event_dispatcher->unsubscribe(this); - } - - this->event_dispatcher = event_dispatcher; - - if (event_dispatcher) - { - event_dispatcher->subscribe(this); - event_dispatcher->subscribe(this); - event_dispatcher->subscribe(this); - event_dispatcher->subscribe(this); - event_dispatcher->subscribe(this); - event_dispatcher->subscribe(this); - } -} - -void mapper::set_control(input::control* control) +void mapper::connect(::event::queue& queue) { - this->control = control; + subscriptions.emplace_back(queue.subscribe(std::bind_front(&mapper::handle_gamepad_axis_moved, this))); + subscriptions.emplace_back(queue.subscribe(std::bind_front(&mapper::handle_gamepad_button_pressed, this))); + subscriptions.emplace_back(queue.subscribe(std::bind_front(&mapper::handle_key_pressed, this))); + subscriptions.emplace_back(queue.subscribe(std::bind_front(&mapper::handle_mouse_button_pressed, this))); + subscriptions.emplace_back(queue.subscribe(std::bind_front(&mapper::handle_mouse_moved, this))); + subscriptions.emplace_back(queue.subscribe(std::bind_front(&mapper::handle_mouse_scrolled, this))); } -void mapper::set_callback(std::function callback) +void mapper::disconnect() { - this->callback = callback; + subscriptions.clear(); } -void mapper::set_enabled(bool enabled) +void mapper::handle_gamepad_axis_moved(const event::gamepad_axis_moved& event) { - this->enabled = enabled; + input_mapped_publisher.publish({std::shared_ptr(new gamepad_axis_mapping(event.gamepad, event.axis, std::signbit(event.position)))}); } -void mapper::handle_event(const key_pressed_event& event) +void mapper::handle_gamepad_button_pressed(const event::gamepad_button_pressed& event) { - if (!is_enabled() || !callback) - { - return; - } - - callback(key_mapping(control, event.keyboard, event.scancode)); + input_mapped_publisher.publish({std::shared_ptr(new gamepad_button_mapping(event.gamepad, event.button))}); } -void mapper::handle_event(const mouse_moved_event& event) +void mapper::handle_key_pressed(const event::key_pressed& event) { - if (!is_enabled() || !callback) - { - return; - } - - if (event.dx != 0) - { - mouse_motion_axis axis = (event.dx < 0) ? mouse_motion_axis::negative_x : mouse_motion_axis::positive_x; - callback(mouse_motion_mapping(control, event.mouse, axis)); - } - - if (event.dy != 0) - { - mouse_motion_axis axis = (event.dy < 0) ? mouse_motion_axis::negative_y : mouse_motion_axis::positive_y; - callback(mouse_motion_mapping(control, event.mouse, axis)); - } + input_mapped_publisher.publish({std::shared_ptr(new key_mapping(event.keyboard, event.scancode))}); } -void mapper::handle_event(const mouse_button_pressed_event& event) +void mapper::handle_mouse_button_pressed(const event::mouse_button_pressed& event) { - if (!is_enabled() || !callback) - { - return; - } - - callback(mouse_button_mapping(control, event.mouse, event.button)); + input_mapped_publisher.publish({std::shared_ptr(new mouse_button_mapping(event.mouse, event.button))}); } -void mapper::handle_event(const mouse_wheel_scrolled_event& event) +void mapper::handle_mouse_moved(const event::mouse_moved& event) { - if (!is_enabled() || !callback) + if (event.difference.x()) { - return; + input_mapped_publisher.publish({std::shared_ptr(new mouse_motion_mapping(event.mouse, mouse_motion_axis::x, std::signbit(static_cast(event.difference.x()))))}); } - - if (event.x != 0) + + if (event.difference.y()) { - mouse_wheel_axis axis = (event.x < 0) ? mouse_wheel_axis::negative_x : mouse_wheel_axis::positive_x; - callback(mouse_wheel_mapping(control, event.mouse, axis)); - } - - if (event.y != 0) - { - mouse_wheel_axis axis = (event.y < 0) ? mouse_wheel_axis::negative_y : mouse_wheel_axis::positive_y; - callback(mouse_wheel_mapping(control, event.mouse, axis)); + input_mapped_publisher.publish({std::shared_ptr(new mouse_motion_mapping(event.mouse, mouse_motion_axis::y, std::signbit(static_cast(event.difference.y()))))}); } } -void mapper::handle_event(const gamepad_button_pressed_event& event) +void mapper::handle_mouse_scrolled(const event::mouse_scrolled& event) { - if (!is_enabled() || !callback) + if (event.velocity.x()) { - return; + input_mapped_publisher.publish({std::shared_ptr(new mouse_scroll_mapping(event.mouse, mouse_scroll_axis::x, std::signbit(event.velocity.x())))}); } - - callback(gamepad_button_mapping(control, event.controller, event.button)); -} - -void mapper::handle_event(const gamepad_axis_moved_event& event) -{ - if (!is_enabled() || !callback) + + if (event.velocity.y()) { - return; + input_mapped_publisher.publish({std::shared_ptr(new mouse_scroll_mapping(event.mouse, mouse_scroll_axis::y, std::signbit(event.velocity.y())))}); } - - callback(gamepad_axis_mapping(control, event.controller, event.axis, (event.value < 0.0f))); } } // namespace input diff --git a/src/input/mapper.hpp b/src/input/mapper.hpp index bd1c1c3..9418e4e 100644 --- a/src/input/mapper.hpp +++ b/src/input/mapper.hpp @@ -20,87 +20,52 @@ #ifndef ANTKEEPER_INPUT_MAPPER_HPP #define ANTKEEPER_INPUT_MAPPER_HPP +#include "event/subscription.hpp" +#include "event/queue.hpp" +#include "event/publisher.hpp" +#include "input/event.hpp" #include "input/mapping.hpp" -#include "event/input-events.hpp" -#include "event/event-handler.hpp" -#include "event/event-dispatcher.hpp" -#include +#include +#include namespace input { /** - * An input mapper takes a control and listens to input events then generates corresponding input mappings which can be added to an input router. + * Listens for input events and generates corresponding input mappings. */ -class mapper: - public event_handler, - public event_handler, - public event_handler, - public event_handler, - public event_handler, - public event_handler +class mapper { public: /** - * Creates an input mapper. - */ - mapper(); - - /** - * Destroys an input mapper. - */ - virtual ~mapper(); - - /** - * Sets the event dispatcher to which this input event router will subscribe itself. - */ - void set_event_dispatcher(event_dispatcher* event_dispatcher); - - /** - * Sets the control for which input mappings will be generated. - * - * @param control ::control for which input mappings will be generated. - */ - void set_control(input::control* control); - - /** - * Sets the callback function to the input mappings generated by this input mapper. + * Connects the input event signals of an event queue to the mapper. * - * @param callback Callback function operates on an input mapping. + * @param queue Event queue to connect. */ - void set_callback(std::function callback); - - /** - * Enables or disables the input mapping generation. - * - * @param enabled Whether to enable input mapping generation or not. - */ - void set_enabled(bool enabled); - + void connect(::event::queue& queue); + /** - * Returns true if input mapping generation is enabled. + * Disconnects all input event signals from the mapper. */ - bool is_enabled() const; + void disconnect(); + + /// Returns the channel through which input mapped events are published. + [[nodiscard]] inline ::event::channel& get_input_mapped_channel() noexcept + { + return input_mapped_publisher.channel(); + } private: - void handle_event(const key_pressed_event& event); - void handle_event(const mouse_moved_event& event); - void handle_event(const mouse_wheel_scrolled_event& event); - void handle_event(const mouse_button_pressed_event& event); - void handle_event(const gamepad_axis_moved_event& event); - void handle_event(const gamepad_button_pressed_event& event); - - event_dispatcher* event_dispatcher; - control* control; - std::function callback; - bool enabled; + void handle_gamepad_axis_moved(const event::gamepad_axis_moved& event); + void handle_gamepad_button_pressed(const event::gamepad_button_pressed& event); + void handle_key_pressed(const event::key_pressed& event); + void handle_mouse_button_pressed(const event::mouse_button_pressed& event); + void handle_mouse_moved(const event::mouse_moved& event); + void handle_mouse_scrolled(const event::mouse_scrolled& event); + + std::vector> subscriptions; + ::event::publisher input_mapped_publisher; }; -inline bool mapper::is_enabled() const -{ - return enabled; -} - } // namespace input #endif // ANTKEEPER_INPUT_MAPPER_HPP - diff --git a/src/input/control-set.cpp b/src/input/mapping-type.hpp similarity index 61% rename from src/input/control-set.cpp rename to src/input/mapping-type.hpp index 6af98d7..b715edf 100644 --- a/src/input/control-set.cpp +++ b/src/input/mapping-type.hpp @@ -17,40 +17,37 @@ * along with Antkeeper source code. If not, see . */ -#include "control-set.hpp" -#include "control.hpp" +#ifndef ANTKEEPER_INPUT_MAPPING_TYPE_HPP +#define ANTKEEPER_INPUT_MAPPING_TYPE_HPP namespace input { -void control_set::add_control(control* control) -{ - controls.push_back(control); -} - -void control_set::remove_control(control* control) -{ - controls.remove(control); -} - -void control_set::remove_controls() -{ - controls.clear(); -} - -void control_set::update() -{ - for (control* control: controls) - { - control->update(); - } -} - -void control_set::set_callbacks_enabled(bool enabled) +/** + * Input mapping types. + * + * @see input::mapping + */ +enum class mapping_type { - for (control* control: controls) - { - control->set_callbacks_enabled(enabled); - } -} + /// Gamepad axis mapping. + gamepad_axis, + + /// Gamepad button mapping. + gamepad_button, + + /// Key mapping. + key, + + /// Mouse button mapping. + mouse_button, + + /// Mouse motion mapping. + mouse_motion, + + /// Mouse scroll mapping. + mouse_scroll +}; } // namespace input + +#endif // ANTKEEPER_INPUT_MAPPING_TYPE_HPP diff --git a/src/input/mapping.cpp b/src/input/mapping.cpp index daea28b..3bf5da6 100644 --- a/src/input/mapping.cpp +++ b/src/input/mapping.cpp @@ -17,128 +17,42 @@ * along with Antkeeper source code. If not, see . */ -#include "mapping.hpp" +#include "input/mapping.hpp" namespace input { -mapping::mapping(input::control* control): - control(control) +gamepad_axis_mapping::gamepad_axis_mapping(input::gamepad* gamepad, gamepad_axis axis, bool direction): + gamepad(gamepad), + axis(axis), + direction(direction) {} -key_mapping::key_mapping(const key_mapping& mapping) -{ - *this = mapping; -} +gamepad_button_mapping::gamepad_button_mapping(input::gamepad* gamepad, gamepad_button button): + gamepad(gamepad), + button(button) +{} -key_mapping::key_mapping(input::control* control, input::keyboard* keyboard, input::scancode scancode): - mapping(control), +key_mapping::key_mapping(input::keyboard* keyboard, input::scancode scancode, bool repeat): keyboard(keyboard), - scancode(scancode) + scancode(scancode), + repeat(repeat) {} -key_mapping& key_mapping::operator=(const key_mapping& mapping) -{ - control = mapping.control; - keyboard = mapping.keyboard; - scancode = mapping.scancode; - return *this; -} - -mouse_motion_mapping::mouse_motion_mapping(const mouse_motion_mapping& mapping) -{ - *this = mapping; -} - -mouse_motion_mapping::mouse_motion_mapping(input::control* control, input::mouse* mouse, mouse_motion_axis axis): - mapping(control), +mouse_button_mapping::mouse_button_mapping(input::mouse* mouse, mouse_button button): mouse(mouse), - axis(axis) + button(button) {} -mouse_motion_mapping& mouse_motion_mapping::operator=(const mouse_motion_mapping& mapping) -{ - control = mapping.control; - mouse = mapping.mouse; - axis = mapping.axis; - return *this; -} - -mouse_wheel_mapping::mouse_wheel_mapping(const mouse_wheel_mapping& mapping) -{ - *this = mapping; -} - -mouse_wheel_mapping::mouse_wheel_mapping(input::control* control, input::mouse* mouse, mouse_wheel_axis axis): - mapping(control), +mouse_motion_mapping::mouse_motion_mapping(input::mouse* mouse, mouse_motion_axis axis, bool direction): mouse(mouse), - axis(axis) + axis(axis), + direction(direction) {} -mouse_wheel_mapping& mouse_wheel_mapping::operator=(const mouse_wheel_mapping& mapping) -{ - control = mapping.control; - mouse = mapping.mouse; - axis = mapping.axis; - return *this; -} - -mouse_button_mapping::mouse_button_mapping(const mouse_button_mapping& mapping) -{ - *this = mapping; -} - -mouse_button_mapping::mouse_button_mapping(input::control* control, input::mouse* mouse, int button): - mapping(control), +mouse_scroll_mapping::mouse_scroll_mapping(input::mouse* mouse, mouse_scroll_axis axis, bool direction): mouse(mouse), - button(button) -{} - -mouse_button_mapping& mouse_button_mapping::operator=(const mouse_button_mapping& mapping) -{ - control = mapping.control; - mouse = mapping.mouse; - button = mapping.button; - return *this; -} - -gamepad_axis_mapping::gamepad_axis_mapping(const gamepad_axis_mapping& mapping) -{ - *this = mapping; -} - -gamepad_axis_mapping::gamepad_axis_mapping(input::control* control, gamepad* controller, gamepad_axis axis, bool negative): - mapping(control), - controller(controller), axis(axis), - negative(negative) + direction(direction) {} -gamepad_axis_mapping& gamepad_axis_mapping::operator=(const gamepad_axis_mapping& mapping) -{ - control = mapping.control; - controller = mapping.controller; - axis = mapping.axis; - negative = mapping.negative; - return *this; -} - -gamepad_button_mapping::gamepad_button_mapping(const gamepad_button_mapping& mapping) -{ - *this = mapping; -} - -gamepad_button_mapping::gamepad_button_mapping(input::control* control, gamepad* controller, gamepad_button button): - mapping(control), - controller(controller), - button(button) -{} - -gamepad_button_mapping& gamepad_button_mapping::operator=(const gamepad_button_mapping& mapping) -{ - control = mapping.control; - controller = mapping.controller; - button = mapping.button; - return *this; -} - } // namespace input diff --git a/src/input/mapping.hpp b/src/input/mapping.hpp index 15dea9c..e104401 100644 --- a/src/input/mapping.hpp +++ b/src/input/mapping.hpp @@ -20,30 +20,21 @@ #ifndef ANTKEEPER_INPUT_MAPPING_HPP #define ANTKEEPER_INPUT_MAPPING_HPP +#include "input/mapping-type.hpp" +#include "input/gamepad-axis.hpp" +#include "input/gamepad-button.hpp" +#include "input/mouse-button.hpp" +#include "input/mouse-motion-axis.hpp" +#include "input/mouse-scroll-axis.hpp" +#include "input/scancode.hpp" +#include + namespace input { -enum class mouse_motion_axis; -enum class mouse_wheel_axis; -enum class scancode; -enum class gamepad_axis; -enum class gamepad_button; class control; +class gamepad; class keyboard; class mouse; -class gamepad; - -/** - * Enumerates the supported types of control mappings. - */ -enum class mapping_type -{ - key, - mouse_motion, - mouse_wheel, - mouse_button, - gamepad_axis, - gamepad_button -}; /** * Abstract base class for input mappings. @@ -51,150 +42,233 @@ enum class mapping_type class mapping { public: + /** + * Constructs an input mapping. + */ mapping() = default; - mapping(input::control* control); + + /// Destructs an input mapping. virtual ~mapping() = default; - - /// Returns this control mapping's type. - virtual mapping_type get_type() const = 0; - - control* control; + + /// Returns the input mapping type. + [[nodiscard]] virtual constexpr mapping_type get_mapping_type() const noexcept = 0; }; /** - * A mapping between a control and a keyboard key. + * Maps a direction along a gamepad axis to a control input value. */ -class key_mapping: public mapping +class gamepad_axis_mapping: public mapping { public: - key_mapping() = default; - key_mapping(const key_mapping& mapping); - key_mapping(input::control* control, input::keyboard* keyboard, scancode scancode); - virtual ~key_mapping() = default; - key_mapping& operator=(const key_mapping& mapping); - virtual mapping_type get_type() const; - - input::keyboard* keyboard; - scancode scancode; + /** + * Constructs a gamepad axis mapping. + * + * @param gamepad Pointer to the gamepad to map, or `nullptr` if input from any gamepad will be mapped. + * @param axis Gamepad axis to map. + * @param direction Sign bit of the direction to map. + */ + /// @{ + gamepad_axis_mapping(input::gamepad* gamepad, gamepad_axis axis, bool direction); + gamepad_axis_mapping() = default; + /// @} + + /// Destructs a gamepad axis mapping. + virtual ~gamepad_axis_mapping() = default; + + /// Returns mapping_type::gamepad_axis. + [[nodiscard]] inline virtual constexpr mapping_type get_mapping_type() const noexcept + { + return mapping_type::gamepad_axis; + } + + /// Pointer to the mapped gamepad, or `nullptr` if input from any gamepad is accepted. + input::gamepad* gamepad; + + /// Mapped gamepad axis. + gamepad_axis axis; + + /// Sign bit of the mapped direction. + bool direction; }; -inline mapping_type key_mapping::get_type() const -{ - return mapping_type::key; -} - /** - * A mapping between a control and a mouse motion axis. + * Maps a gamepad button to a control input value. */ -class mouse_motion_mapping: public mapping +class gamepad_button_mapping: public mapping { public: - mouse_motion_mapping() = default; - mouse_motion_mapping(const mouse_motion_mapping& mapping); - mouse_motion_mapping(input::control* control, input::mouse* mouse, mouse_motion_axis axis); - virtual ~mouse_motion_mapping() = default; - mouse_motion_mapping& operator=(const mouse_motion_mapping& mapping); - virtual mapping_type get_type() const; - - input::mouse* mouse; - mouse_motion_axis axis; + /** + * Constructs a gamepad button mapping. + * + * @param gamepad Pointer to the gamepad to map, or `nullptr` if input from any gamepad will be mapped. + * @param button Gamepad button to map. + */ + /// @{ + gamepad_button_mapping(input::gamepad* gamepad, gamepad_button button); + gamepad_button_mapping() = default; + /// @} + + /// Destructs a gamepad button mapping. + virtual ~gamepad_button_mapping() = default; + + /// Returns mapping_type::gamepad_button. + [[nodiscard]] inline virtual constexpr mapping_type get_mapping_type() const noexcept + { + return mapping_type::gamepad_button; + } + + /// Pointer to the mapped gamepad, or `nullptr` if input from any gamepad is accepted. + input::gamepad* gamepad; + + /// Mapped gamepad button. + gamepad_button button; }; -inline mapping_type mouse_motion_mapping::get_type() const -{ - return mapping_type::mouse_motion; -} - /** - * A mapping between a control and a mouse wheel axis. + * Maps a keyboard key to a control input value. */ -class mouse_wheel_mapping: public mapping +class key_mapping: public mapping { public: - mouse_wheel_mapping() = default; - mouse_wheel_mapping(const mouse_wheel_mapping& mapping); - mouse_wheel_mapping(input::control* control, input::mouse* mouse, mouse_wheel_axis axis); - virtual ~mouse_wheel_mapping() = default; - mouse_wheel_mapping& operator=(const mouse_wheel_mapping& mapping); - virtual mapping_type get_type() const; - - input::mouse* mouse; - mouse_wheel_axis axis; + /** + * Constructs a key mapping. + * + * @param keyboard Pointer to the keyboard to map, or `nullptr` if input from any keyboard will be mapped. + * @param scancode Scancode of the key to map. + * @param repeat `false` if the mapping should ignore key repeats, `true` otherwise. + */ + /// @{ + key_mapping(input::keyboard* keyboard, input::scancode scancode, bool repeat = false); + key_mapping() = default; + /// @} + + /// Destructs a keyboard key mapping. + virtual ~key_mapping() = default; + + /// Returns mapping_type::key. + [[nodiscard]] inline virtual constexpr mapping_type get_mapping_type() const noexcept + { + return mapping_type::key; + } + + /// Pointer to the mapped keyboard, or `nullptr` if input from any keyboard is accepted. + input::keyboard* keyboard; + + /// Scancode of the mapped key. + scancode scancode; + + /// `false` if the mapping ignores key repeats, `true` otherwise. + bool repeat; }; -inline mapping_type mouse_wheel_mapping::get_type() const -{ - return mapping_type::mouse_wheel; -} - /** - * A mapping between a control and a mouse button. + * Maps a mouse button to a control input value. */ class mouse_button_mapping: public mapping { public: + /** + * Constructs a mouse button mapping. + * + * @param mouse Pointer to the mouse to map, or `nullptr` if input from any mouse will be mapped. + * @param button Mouse button to map. + */ + /// @{ + mouse_button_mapping(input::mouse* mouse, mouse_button button); mouse_button_mapping() = default; - mouse_button_mapping(const mouse_button_mapping& mapping); - mouse_button_mapping(input::control* control, input::mouse* mouse, int button); + /// @} + + /// Destructs a mouse button mapping. virtual ~mouse_button_mapping() = default; - mouse_button_mapping& operator=(const mouse_button_mapping& mapping); - virtual mapping_type get_type() const; - + + /// Returns mapping_type::mouse_button. + [[nodiscard]] inline virtual constexpr mapping_type get_mapping_type() const noexcept + { + return mapping_type::mouse_button; + } + + /// Pointer to the mapped mouse, or `nullptr` if input from any mouse is accepted. input::mouse* mouse; - int button; + + /// Mapped mouse button. + mouse_button button; }; -inline mapping_type mouse_button_mapping::get_type() const -{ - return mapping_type::mouse_button; -} - /** - * A mapping between a control and a gamepad axis. + * Maps a direction along a mouse motion axis to a control input value. */ -class gamepad_axis_mapping: public mapping +class mouse_motion_mapping: public mapping { public: - gamepad_axis_mapping() = default; - gamepad_axis_mapping(const gamepad_axis_mapping& mapping); - gamepad_axis_mapping(input::control* control, gamepad* controller, gamepad_axis axis, bool negative); - virtual ~gamepad_axis_mapping() = default; - gamepad_axis_mapping& operator=(const gamepad_axis_mapping& mapping); - virtual mapping_type get_type() const; - - gamepad* controller; - gamepad_axis axis; - bool negative; + /** + * Constructs a mouse motion mapping. + * + * @param mouse Pointer to the mouse to map, or `nullptr` if input from any mouse will be mapped. + * @param axis Mouse motion axis to map. + * @param direction Sign bit of the direction to map. + */ + /// @{ + mouse_motion_mapping(input::mouse* mouse, mouse_motion_axis axis, bool direction); + mouse_motion_mapping() = default; + /// @} + + /// Destructs a mouse motion mapping. + virtual ~mouse_motion_mapping() = default; + + /// Returns mapping_type::mouse_motion. + [[nodiscard]] inline virtual constexpr mapping_type get_mapping_type() const noexcept + { + return mapping_type::mouse_motion; + } + + /// Pointer to the mapped mouse, or `nullptr` if input from any mouse is accepted. + input::mouse* mouse; + + /// Mapped mouse motion axis. + mouse_motion_axis axis; + + /// Sign bit of the mapped direction. + bool direction; }; -inline mapping_type gamepad_axis_mapping::get_type() const -{ - return mapping_type::gamepad_axis; -} - /** - * A mapping between a control and a gamepad button. + * Maps a direction along a mouse scroll axis to a control input value. */ -class gamepad_button_mapping: public mapping +class mouse_scroll_mapping: public mapping { public: - gamepad_button_mapping() = default; - gamepad_button_mapping(const gamepad_button_mapping& mapping); - gamepad_button_mapping(input::control* control, gamepad* controller, gamepad_button button); - virtual ~gamepad_button_mapping() = default; - gamepad_button_mapping& operator=(const gamepad_button_mapping& mapping); - virtual mapping_type get_type() const; - - gamepad* controller; - gamepad_button button; + /** + * Constructs a mouse scroll mapping. + * + * @param control Control to which input will be mapped. + * @param mouse Pointer to the mouse to map, or `nullptr` if input from any mouse will be mapped. + * @param axis Mouse scroll axis to map. + * @param direction Sign bit of the direction to map. + */ + /// @{ + mouse_scroll_mapping(input::mouse* mouse, mouse_scroll_axis axis, bool direction); + mouse_scroll_mapping() = default; + /// @} + + /// Destructs a mouse scroll mapping. + virtual ~mouse_scroll_mapping() = default; + + /// Returns mapping_type::mouse_scroll. + [[nodiscard]] inline virtual constexpr mapping_type get_mapping_type() const noexcept + { + return mapping_type::mouse_scroll; + } + + /// Pointer to the mapped mouse, or `nullptr` if input from any mouse is accepted. + input::mouse* mouse; + + /// Mapped mouse scroll axis. + mouse_scroll_axis axis; + + /// Sign bit of the mapped direction. + bool direction; }; -inline mapping_type gamepad_button_mapping::get_type() const -{ - return mapping_type::gamepad_button; -} - } // namespace input #endif // ANTKEEPER_INPUT_MAPPING_HPP - diff --git a/src/input/modifier-key.hpp b/src/input/modifier-key.hpp new file mode 100644 index 0000000..0e8e5e3 --- /dev/null +++ b/src/input/modifier-key.hpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_INPUT_MODIFIER_KEY_HPP +#define ANTKEEPER_INPUT_MODIFIER_KEY_HPP + +#include + +namespace input { + +/** + * Modifier key bit mask flags. + */ +namespace modifier_key { + +/// Modifier key bit flags. +enum: std::uint16_t +{ + /// No modifier key is pressed. + none = 0b0000000000000000, + + /// Left shift modifier key is pressed. + left_shift = 0b0000000000000001, + + /// Right shift modifier key is pressed. + right_shift = 0b0000000000000010, + + /// One or both shift modifier keys are pressed. + shift = left_shift | right_shift, + + /// Left ctrl modifier key is pressed. + left_ctrl = 0b0000000000000100, + + /// Right ctrl modifier key is pressed. + right_ctrl = 0b0000000000001000, + + /// One or both ctrl modifier keys are pressed. + ctrl = left_ctrl | right_ctrl, + + /// Left alt modifier key is pressed. + left_alt = 0b0000000000010000, + + /// Right alt modifier key is pressed. + right_alt = 0b0000000000100000, + + /// One or both alt modifier keys are pressed. + alt = left_alt | right_alt, + + /// Left gui modifier key is pressed. + left_gui = 0b0000000001000000, + + /// Right gui modifier key is pressed. + right_gui = 0b0000000010000000, + + /// One or both gui modifier keys are pressed. + gui = left_gui | right_gui, + + /// Num lock modifier key is pressed. + num_lock = 0b0000000100000000, + + /// Caps lock modifier key is pressed. + caps_lock = 0b0000001000000000, + + /// Scroll lock modifier key is pressed. + scroll_lock = 0b0000010000000000, + + /// AltGr modifier key is pressed. + alt_gr = 0b0000100000000000 +}; + +} // namespace modifier_key +} // namespace input + +#endif // ANTKEEPER_INPUT_MODIFIER_KEY_HPP diff --git a/src/input/mouse-button.hpp b/src/input/mouse-button.hpp new file mode 100644 index 0000000..5c0dc0d --- /dev/null +++ b/src/input/mouse-button.hpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_INPUT_MOUSE_BUTTON_HPP +#define ANTKEEPER_INPUT_MOUSE_BUTTON_HPP + +#include + +namespace input { + +/// Mouse buttons. +enum class mouse_button: std::uint8_t +{ + /// Left mouse button. + left = 1, + + /// Middle mouse button. + middle = 2, + + /// Right mouse button. + right = 3 +}; + +} // namespace input + +#endif // ANTKEEPER_INPUT_MOUSE_BUTTON_HPP diff --git a/src/input/sdl-game-controller-tables.hpp b/src/input/mouse-motion-axis.hpp similarity index 73% rename from src/input/sdl-game-controller-tables.hpp rename to src/input/mouse-motion-axis.hpp index 94d779d..8ffb4b1 100644 --- a/src/input/sdl-game-controller-tables.hpp +++ b/src/input/mouse-motion-axis.hpp @@ -17,18 +17,23 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_INPUT_SDL_GAMEPAD_TABLES_HPP -#define ANTKEEPER_INPUT_SDL_GAMEPAD_TABLES_HPP +#ifndef ANTKEEPER_INPUT_MOUSE_MOTION_AXIS_HPP +#define ANTKEEPER_INPUT_MOUSE_MOTION_AXIS_HPP -namespace input { +#include -enum class gamepad_button; -enum class gamepad_axis; +namespace input { -extern const gamepad_button sdl_button_table[15]; -extern const gamepad_axis sdl_axis_table[6]; +/// Mouse motion axes. +enum class mouse_motion_axis: std::uint8_t +{ + /// X-axis. + x = 0, + + /// Y-axis. + y = 1 +}; } // namespace input -#endif // ANTKEEPER_INPUT_SDL_GAMEPAD_TABLES_HPP - +#endif // ANTKEEPER_INPUT_MOUSE_MOTION_AXIS_HPP diff --git a/src/event/window-events.cpp b/src/input/mouse-scroll-axis.hpp similarity index 71% rename from src/event/window-events.cpp rename to src/input/mouse-scroll-axis.hpp index 54ec151..05a84d4 100644 --- a/src/event/window-events.cpp +++ b/src/input/mouse-scroll-axis.hpp @@ -17,12 +17,23 @@ * along with Antkeeper source code. If not, see . */ -#include "event/window-events.hpp" +#ifndef ANTKEEPER_INPUT_MOUSE_SCROLL_AXIS_HPP +#define ANTKEEPER_INPUT_MOUSE_SCROLL_AXIS_HPP -event_base* window_resized_event::clone() const +#include + +namespace input { + +/// Mouse scroll axes. +enum class mouse_scroll_axis: std::uint8_t { - window_resized_event* event = new window_resized_event(); - event->w = w; - event->h = h; - return event; -} + /// X-axis. + x = 0, + + /// Y-axis. + y = 1 +}; + +} // namespace input + +#endif // ANTKEEPER_INPUT_MOUSE_SCROLL_AXIS_HPP diff --git a/src/input/mouse.cpp b/src/input/mouse.cpp index 60433ca..2121867 100644 --- a/src/input/mouse.cpp +++ b/src/input/mouse.cpp @@ -17,80 +17,29 @@ * along with Antkeeper source code. If not, see . */ -#include "mouse.hpp" -#include "event/input-events.hpp" -#include "event/event-dispatcher.hpp" +#include "input/mouse.hpp" namespace input { -mouse::mouse() -{} - -void mouse::press(int button, int x, int y) +void mouse::press(mouse_button button) { - if (!device::event_dispatcher) - { - return; - } - - mouse_button_pressed_event event; - event.mouse = this; - event.button = button; - event.x = x; - event.y = y; - - device::event_dispatcher->queue(event); + button_pressed_publisher.publish({this, position, button}); } -void mouse::release(int button, int x, int y) +void mouse::release(mouse_button button) { - if (!device::event_dispatcher) - { - return; - } - - mouse_button_released_event event; - event.mouse = this; - event.button = button; - event.x = x; - event.y = y; - - device::event_dispatcher->queue(event); + button_released_publisher.publish({this, position, button}); } -void mouse::move(int x, int y, int dx, int dy) +void mouse::move(const math::vector& position, const math::vector& difference) { - previous_position = current_position; - current_position = {x, y}; - - if (!device::event_dispatcher) - { - return; - } - - mouse_moved_event event; - event.mouse = this; - event.x = x; - event.y = y; - event.dx = dx; - event.dy = dy; - - device::event_dispatcher->queue(event); + this->position = position; + moved_publisher.publish({this, position, difference}); } -void mouse::scroll(int x, int y) +void mouse::scroll(const math::vector& velocity) { - if (!device::event_dispatcher) - { - return; - } - - mouse_wheel_scrolled_event event; - event.mouse = this; - event.x = x; - event.y = y; - - device::event_dispatcher->queue(event); + scrolled_publisher.publish({this, position, velocity}); } } // namespace input diff --git a/src/input/mouse.hpp b/src/input/mouse.hpp index b4ef78f..71bc3aa 100644 --- a/src/input/mouse.hpp +++ b/src/input/mouse.hpp @@ -20,116 +20,103 @@ #ifndef ANTKEEPER_INPUT_MOUSE_HPP #define ANTKEEPER_INPUT_MOUSE_HPP -#include "device.hpp" -#include +#include "input/device.hpp" +#include "input/event.hpp" +#include "input/mouse-button.hpp" +#include "event/publisher.hpp" +#include "math/vector.hpp" +#include namespace input { /** - * Enumerates the mouse motion axes. - */ -enum class mouse_motion_axis -{ - /// Indicates the positive X-axis. - positive_x, - - /// Indicates the negative X-axis. - negative_x, - - /// Indicates the positive Y-axis. - positive_y, - - /// Indicates the negative Y-axis. - negative_y -}; - -/** - * Enumerates the mouse wheel axes. - */ -enum class mouse_wheel_axis -{ - /// Indicates the positive X-axis. - positive_x, - - /// Indicates the negative X-axis. - negative_x, - - /// Indicates the positive Y-axis. - positive_y, - - /// Indicates the negative Y-axis. - negative_y -}; - -/** - * A virtual mouse which can generate mouse-related input events and pass them to an event dispatcher. + * A virtual mouse which generates mouse-related input events. */ class mouse: public device { public: /** - * Creates a mouse input device. + * Constructs a mouse input device. */ - mouse(); - - /// Destroys a mouse input device. + mouse() = default; + + /// Destructs a mouse input device. virtual ~mouse() = default; /** * Simulates a mouse button press. * - * @param button Index of the pressed button. - * @param x X-coordinate of the mouse position. - * @param y Y-coordinate of the mouse position. + * @param button Button to press. */ - void press(int button, int x, int y); - + void press(mouse_button button); + /** * Simulates a mouse button release. * - * @param button Index of the released button. - * @param x X-coordinate of the mouse position. - * @param y Y-coordinate of the mouse position. + * @param button Button to release. */ - void release(int button, int x, int y); - + void release(mouse_button button); + /** * Simulates mouse movement. * - * @param x X-coordinate of the mouse position. - * @param y Y-coordinate of the mouse position. - * @param dx Relative movement on the X-axis. - * @param dy Relative movement on the Y-axis. + * @param position Mouse position, in pixels, relative to the window. + * @param difference Relative movement of the mouse, in pixels. */ - void move(int x, int y, int dx, int dy); - + void move(const math::vector& position, const math::vector& difference); + /** - * Simulates mouse wheel scrolling. + * Simulates mouse scrolling. + * + * @param velocity Scroll velocity. */ - void scroll(int x, int y); + void scroll(const math::vector& velocity); - /// Returns the current mouse position. - const std::tuple& get_current_position() const; - - /// Returns the previous mouse position. - const std::tuple& get_previous_position() const; + /// Returns the current mouse position, in pixels, relative to the window. + [[nodiscard]] inline const math::vector& get_position() const noexcept + { + return position; + } + + /// Returns the channel through which mouse button pressed events are published. + [[nodiscard]] inline ::event::channel& get_button_pressed_channel() noexcept + { + return button_pressed_publisher.channel(); + } + + /// Returns the channel through which mouse button released events are published. + [[nodiscard]] inline ::event::channel& get_button_released_channel() noexcept + { + return button_released_publisher.channel(); + } + + /// Returns the channel through which mouse moved events are published. + [[nodiscard]] inline ::event::channel& get_moved_channel() noexcept + { + return moved_publisher.channel(); + } + + /// Returns the channel through which mouse scrolled events are published. + [[nodiscard]] inline ::event::channel& get_scrolled_channel() noexcept + { + return scrolled_publisher.channel(); + } + + /// Returns device_type::mouse. + [[nodiscard]] inline virtual constexpr device_type get_device_type() const noexcept + { + return device_type::mouse; + } private: - std::tuple current_position; - std::tuple previous_position; + math::vector position; + + ::event::publisher button_pressed_publisher; + ::event::publisher button_released_publisher; + ::event::publisher moved_publisher; + ::event::publisher scrolled_publisher; }; -inline const std::tuple& mouse::get_current_position() const -{ - return current_position; -} - -inline const std::tuple& mouse::get_previous_position() const -{ - return previous_position; -} - } // namespace input #endif // ANTKEEPER_INPUT_MOUSE_HPP - diff --git a/src/input/scancode.hpp b/src/input/scancode.hpp index aa83f61..dfb4d7b 100644 --- a/src/input/scancode.hpp +++ b/src/input/scancode.hpp @@ -20,262 +20,252 @@ #ifndef ANTKEEPER_INPUT_SCANCODE_HPP #define ANTKEEPER_INPUT_SCANCODE_HPP +#include + namespace input { /** - * Enumerates keyboard scancodes. + * Keyboard scancodes. + * + * @see HID Usage Tables for Universal Serial Bus (USB) version 1.3, 2022, https://usb.org/sites/default/files/hut1_3_0.pdf. */ -enum class scancode +enum class scancode: std::uint16_t { - unknown, - a, - b, - c, - d, - e, - f, - g, - h, - i, - j, - k, - l, - m, - n, - o, - p, - q, - r, - s, - t, - u, - v, - w, - x, - y, - z, - one, - two, - three, - four, - five, - six, - seven, - eight, - nine, - zero, - enter, - escape, - backspace, - tab, - space, - minus, - equal, - left_brace, - right_brace, - backslash, - non_us_hash, - semicolon, - apostrophe, - grave, - comma, - dot, - slash, - caps_lock, - f1, - f2, - f3, - f4, - f5, - f6, - f7, - f8, - f9, - f10, - f11, - f12, - print_screen, - scroll_lock, - pause, - insert, - home, - page_up, - del, - end, - page_down, - right, - left, - down, - up, - num_lock, - kp_slash, - kp_asterisk, - kp_minus, - kp_plus, - kp_enter, - kp_1, - kp_2, - kp_3, - kp_4, - kp_5, - kp_6, - kp_7, - kp_8, - kp_9, - kp_0, - kp_dot, - non_us_backslash, - application, - power, - kp_equal, - f13, - f14, - f15, - f16, - f17, - f18, - f19, - f20, - f21, - f22, - f23, - f24, - execute, - help, - menu, - select, - stop, - again, - undo, - cut, - copy, - paste, - find, - mute, - volume_up, - volume_down, - locking_caps_lock, - locking_num_lock, - locking_scroll_lock, - kp_comma, - kp_equal_as400, - international_1, - international_2, - international_3, - international_4, - international_5, - international_6, - international_7, - international_8, - international_9, - lang_1, - lang_2, - lang_3, - lang_4, - lang_5, - lang_6, - lang_7, - lang_8, - lang_9, - alt_erase, - sys_req, - cancel, - clear, - prior, - return_2, - separator, - _out, - oper, - clear_again, - cr_sel, - ex_sel, - kp_00, - kp_000, - thousands_separator, - decimal_separator, - currency_unit, - currency_sub_unit, - kp_left_paren, - kp_right_paren, - kp_left_brace, - kp_right_brace, - kp_tab, - kp_backspace, - kp_a, - kp_b, - kp_c, - kp_d, - kp_e, - kp_f, - kp_xor, - kp_power, - kp_percent, - kp_less, - kp_greater, - kp_ampersand, - kp_double_ampersand, - kp_vertical_bar, - kp_double_vertical_bar, - kp_colon, - kp_hash, - kp_space, - kp_at, - kp_exclam, - kp_mem_store, - kp_mem_recall, - kp_mem_clear, - kp_mem_add, - kp_mem_subtract, - kp_mem_multiply, - kp_mem_divide, - kp_plus_minus, - kp_clear, - kp_clear_entry, - kp_binary, - kp_octal, - kp_decimal, - kp_hexadecimal, - left_ctrl, - left_shift, - left_alt, - left_gui, - right_ctrl, - right_shift, - right_alt, - right_gui, - mode, - audio_next, - audio_prev, - audio_stop, - audio_play, - audio_mute, - media_select, - www, - mail, - calculator, - computer, - ac_search, - ac_home, - ac_back, - ac_forward, - ac_stop, - ac_refresh, - ac_bookmarks, - brightness_down, - brightness_up, - display_switch, - kbd_illum_toggle, - kbd_illum_down, - kbd_illum_up, - eject, - sleep, - app_1, - app_2, - audio_rewind, - audio_fast_forward + // reserved = 0x00, + error_roll_over = 0x01, + post_fail = 0x02, + error_undefined = 0x03, + a = 0x04, + b = 0x05, + c = 0x06, + d = 0x07, + e = 0x08, + f = 0x09, + g = 0x0A, + h = 0x0B, + i = 0x0C, + j = 0x0D, + k = 0x0E, + l = 0x0F, + m = 0x10, + n = 0x11, + o = 0x12, + p = 0x13, + q = 0x14, + r = 0x15, + s = 0x16, + t = 0x17, + u = 0x18, + v = 0x19, + w = 0x1A, + x = 0x1B, + y = 0x1C, + z = 0x1D, + digit_1 = 0x1E, + digit_2 = 0x1F, + digit_3 = 0x20, + digit_4 = 0x21, + digit_5 = 0x22, + digit_6 = 0x23, + digit_7 = 0x24, + digit_8 = 0x25, + digit_9 = 0x26, + digit_0 = 0x27, + enter = 0x28, + escape = 0x29, + backspace = 0x2A, + tab = 0x2B, + space = 0x2C, + minus = 0x2D, + equal = 0x2E, + left_brace = 0x2F, + right_brace = 0x30, + backslash = 0x31, + non_us_hash = 0x32, + semicolon = 0x33, + apostrophe = 0x34, + grave = 0x35, + comma = 0x36, + dot = 0x37, + slash = 0x38, + caps_lock = 0x39, + f1 = 0x3A, + f2 = 0x3B, + f3 = 0x3C, + f4 = 0x3D, + f5 = 0x3E, + f6 = 0x3F, + f7 = 0x40, + f8 = 0x41, + f9 = 0x42, + f10 = 0x43, + f11 = 0x44, + f12 = 0x45, + print_screen = 0x46, + scroll_lock = 0x47, + pause = 0x48, + insert = 0x49, + home = 0x4A, + page_up = 0x4B, + del = 0x4C, + end = 0x4D, + page_down = 0x4E, + right = 0x4F, + left = 0x50, + down = 0x51, + up = 0x52, + num_lock = 0x53, + kp_slash = 0x54, + kp_asterisk = 0x55, + kp_minus = 0x56, + kp_plus = 0x57, + kp_enter = 0x58, + kp_1 = 0x59, + kp_2 = 0x5A, + kp_3 = 0x5B, + kp_4 = 0x5C, + kp_5 = 0x5D, + kp_6 = 0x5E, + kp_7 = 0x5F, + kp_8 = 0x60, + kp_9 = 0x61, + kp_0 = 0x62, + kp_dot = 0x63, + non_us_backslash = 0x64, + application = 0x65, + power = 0x66, + kp_equal = 0x67, + f13 = 0x68, + f14 = 0x69, + f15 = 0x6A, + f16 = 0x6B, + f17 = 0x6C, + f18 = 0x6D, + f19 = 0x6E, + f20 = 0x6F, + f21 = 0x70, + f22 = 0x71, + f23 = 0x72, + f24 = 0x73, + execute = 0x74, + help = 0x75, + menu = 0x76, + select = 0x77, + stop = 0x78, + again = 0x79, + undo = 0x7A, + cut = 0x7B, + copy = 0x7C, + paste = 0x7D, + find = 0x7E, + mute = 0x7F, + volume_up = 0x80, + volume_down = 0x81, + locking_caps_lock = 0x82, + locking_num_lock = 0x83, + locking_scroll_lock = 0x84, + kp_comma = 0x85, + kp_equal_as400 = 0x86, + international_1 = 0x87, + international_2 = 0x88, + international_3 = 0x89, + international_4 = 0x8A, + international_5 = 0x8B, + international_6 = 0x8C, + international_7 = 0x8D, + international_8 = 0x8E, + international_9 = 0x8F, + lang_1 = 0x90, + lang_2 = 0x91, + lang_3 = 0x92, + lang_4 = 0x93, + lang_5 = 0x94, + lang_6 = 0x95, + lang_7 = 0x96, + lang_8 = 0x97, + lang_9 = 0x98, + alt_erase = 0x99, + sys_req = 0x9A, + cancel = 0x9B, + clear = 0x9C, + prior = 0x9D, + return_2 = 0x9E, + separator = 0x9F, + _out = 0xA0, + oper = 0xA1, + clear_again = 0xA2, + cr_sel = 0xA3, + ex_sel = 0xA4, + // reserved = 0xA5, + // reserved = 0xA6, + // reserved = 0xA7, + // reserved = 0xA8, + // reserved = 0xA9, + // reserved = 0xAA, + // reserved = 0xAB, + // reserved = 0xAC, + // reserved = 0xAD, + // reserved = 0xAE, + // reserved = 0xAF, + kp_00 = 0xB0, + kp_000 = 0xB1, + thousands_separator = 0xB2, + decimal_separator = 0xB3, + currency_unit = 0xB4, + currency_sub_unit = 0xB5, + kp_left_paren = 0xB6, + kp_right_paren = 0xB7, + kp_left_brace = 0xB8, + kp_right_brace = 0xB9, + kp_tab = 0xBA, + kp_backspace = 0xBB, + kp_a = 0xBC, + kp_b = 0xBD, + kp_c = 0xBE, + kp_d = 0xBF, + kp_e = 0xC0, + kp_f = 0xC1, + kp_xor = 0xC2, + kp_power = 0xC3, + kp_percent = 0xC4, + kp_less = 0xC5, + kp_greater = 0xC6, + kp_ampersand = 0xC7, + kp_double_ampersand = 0xC8, + kp_vertical_bar = 0xC9, + kp_double_vertical_bar = 0xCA, + kp_colon = 0xCB, + kp_hash = 0xCC, + kp_space = 0xCD, + kp_at = 0xCE, + kp_exclam = 0xCF, + kp_mem_store = 0xD0, + kp_mem_recall = 0xD1, + kp_mem_clear = 0xD2, + kp_mem_add = 0xD3, + kp_mem_subtract = 0xD4, + kp_mem_multiply = 0xD5, + kp_mem_divide = 0xD6, + kp_plus_minus = 0xD7, + kp_clear = 0xD8, + kp_clear_entry = 0xD9, + kp_binary = 0xDA, + kp_octal = 0xDB, + kp_decimal = 0xDC, + kp_hexadecimal = 0xDD, + // reserved = 0xDE, + // reserved = 0xDF, + left_ctrl = 0xE0, + left_shift = 0xE1, + left_alt = 0xE2, + left_gui = 0xE3, + right_ctrl = 0xE4, + right_shift = 0xE5, + right_alt = 0xE6, + right_gui = 0xE7 + // reserved = 0xE8-0xFFFF }; } // namespace input #endif // ANTKEEPER_INPUT_SCANCODE_HPP - diff --git a/src/input/sdl-game-controller-tables.cpp b/src/input/sdl-game-controller-tables.cpp deleted file mode 100644 index 118c421..0000000 --- a/src/input/sdl-game-controller-tables.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "sdl-game-controller-tables.hpp" -#include "gamepad.hpp" - -namespace input { - -const gamepad_button sdl_button_table[15] = -{ - gamepad_button::a, // SDL_CONTROLLER_BUTTON_A, - gamepad_button::b, // SDL_CONTROLLER_BUTTON_B, - gamepad_button::x, // SDL_CONTROLLER_BUTTON_X, - gamepad_button::y, // SDL_CONTROLLER_BUTTON_Y, - gamepad_button::back, // SDL_CONTROLLER_BUTTON_BACK, - gamepad_button::guide, // SDL_CONTROLLER_BUTTON_GUIDE, - gamepad_button::start, // SDL_CONTROLLER_BUTTON_START, - gamepad_button::left_stick, // SDL_CONTROLLER_BUTTON_LEFTSTICK, - gamepad_button::right_stick, // SDL_CONTROLLER_BUTTON_RIGHTSTICK, - gamepad_button::left_shoulder, // SDL_CONTROLLER_BUTTON_LEFTSHOULDER, - gamepad_button::right_shoulder, // SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, - gamepad_button::dpad_up, // SDL_CONTROLLER_BUTTON_DPAD_UP, - gamepad_button::dpad_down, // SDL_CONTROLLER_BUTTON_DPAD_DOWN, - gamepad_button::dpad_left, // SDL_CONTROLLER_BUTTON_DPAD_LEFT, - gamepad_button::dpad_right, // SDL_CONTROLLER_BUTTON_DPAD_RIGHT, -}; - -const gamepad_axis sdl_axis_table[6] = -{ - gamepad_axis::left_x, // SDL_CONTROLLER_AXIS_LEFTX, - gamepad_axis::left_y, // SDL_CONTROLLER_AXIS_LEFTY, - gamepad_axis::right_x, // SDL_CONTROLLER_AXIS_RIGHTX, - gamepad_axis::right_y, // SDL_CONTROLLER_AXIS_RIGHTY, - gamepad_axis::left_trigger, // SDL_CONTROLLER_AXIS_TRIGGERLEFT, - gamepad_axis::right_trigger, // SDL_CONTROLLER_AXIS_TRIGGERRIGHT, -}; - -} // namespace input diff --git a/src/input/sdl-scancode-table.cpp b/src/input/sdl-scancode-table.cpp deleted file mode 100644 index 88c5a3b..0000000 --- a/src/input/sdl-scancode-table.cpp +++ /dev/null @@ -1,316 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "sdl-scancode-table.hpp" -#include "scancode.hpp" - -namespace input { - -const scancode sdl_scancode_table[287] = -{ - scancode::unknown, // SDL_SCANCODE_UNKNOWN = 0, - scancode::unknown, // Unassigned = 1 - scancode::unknown, // Unassigned = 2 - scancode::unknown, // Unassigned = 3 - scancode::a, // SDL_SCANCODE_A = 4, - scancode::b, // SDL_SCANCODE_B = 5, - scancode::c, // SDL_SCANCODE_C = 6, - scancode::d, // SDL_SCANCODE_D = 7, - scancode::e, // SDL_SCANCODE_E = 8, - scancode::f, // SDL_SCANCODE_F = 9, - scancode::g, // SDL_SCANCODE_G = 10, - scancode::h, // SDL_SCANCODE_H = 11, - scancode::i, // SDL_SCANCODE_I = 12, - scancode::j, // SDL_SCANCODE_J = 13, - scancode::k, // SDL_SCANCODE_K = 14, - scancode::l, // SDL_SCANCODE_L = 15, - scancode::m, // SDL_SCANCODE_M = 16, - scancode::n, // SDL_SCANCODE_N = 17, - scancode::o, // SDL_SCANCODE_O = 18, - scancode::p, // SDL_SCANCODE_P = 19, - scancode::q, // SDL_SCANCODE_Q = 20, - scancode::r, // SDL_SCANCODE_R = 21, - scancode::s, // SDL_SCANCODE_S = 22, - scancode::t, // SDL_SCANCODE_T = 23, - scancode::u, // SDL_SCANCODE_U = 24, - scancode::v, // SDL_SCANCODE_V = 25, - scancode::w, // SDL_SCANCODE_W = 26, - scancode::x, // SDL_SCANCODE_X = 27, - scancode::y, // SDL_SCANCODE_Y = 28, - scancode::z, // SDL_SCANCODE_Z = 29, - scancode::one, // SDL_SCANCODE_1 = 30, - scancode::two, // SDL_SCANCODE_2 = 31, - scancode::three, // SDL_SCANCODE_3 = 32, - scancode::four, // SDL_SCANCODE_4 = 33, - scancode::five, // SDL_SCANCODE_5 = 34, - scancode::six, // SDL_SCANCODE_6 = 35, - scancode::seven, // SDL_SCANCODE_7 = 36, - scancode::eight, // SDL_SCANCODE_8 = 37, - scancode::nine, // SDL_SCANCODE_9 = 38, - scancode::zero, // SDL_SCANCODE_0 = 39, - scancode::enter, // SDL_SCANCODE_RETURN = 40, - scancode::escape, // SDL_SCANCODE_ESCAPE = 41, - scancode::backspace, // SDL_SCANCODE_BACKSPACE = 42, - scancode::tab, // SDL_SCANCODE_TAB = 43, - scancode::space, // SDL_SCANCODE_SPACE = 44, - scancode::minus, // SDL_SCANCODE_MINUS = 45, - scancode::equal, // SDL_SCANCODE_EQUALS = 46, - scancode::left_brace, // SDL_SCANCODE_LEFTBRACKET = 47, - scancode::right_brace, // SDL_SCANCODE_RIGHTBRACKET = 48, - scancode::backslash, // SDL_SCANCODE_BACKSLASH = 49, - scancode::non_us_hash, // SDL_SCANCODE_NONUSHASH = 50, - scancode::semicolon, // SDL_SCANCODE_SEMICOLON = 51, - scancode::apostrophe, // SDL_SCANCODE_APOSTROPHE = 52, - scancode::grave, // SDL_SCANCODE_GRAVE = 53, - scancode::comma, // SDL_SCANCODE_COMMA = 54, - scancode::dot, // SDL_SCANCODE_PERIOD = 55, - scancode::slash, // SDL_SCANCODE_SLASH = 56, - scancode::caps_lock, // SDL_SCANCODE_CAPSLOCK = 57, - scancode::f1, // SDL_SCANCODE_F1 = 58, - scancode::f2, // SDL_SCANCODE_F2 = 59, - scancode::f3, // SDL_SCANCODE_F3 = 60, - scancode::f4, // SDL_SCANCODE_F4 = 61, - scancode::f5, // SDL_SCANCODE_F5 = 62, - scancode::f6, // SDL_SCANCODE_F6 = 63, - scancode::f7, // SDL_SCANCODE_F7 = 64, - scancode::f8, // SDL_SCANCODE_F8 = 65, - scancode::f9, // SDL_SCANCODE_F9 = 66, - scancode::f10, // SDL_SCANCODE_F10 = 67, - scancode::f11, // SDL_SCANCODE_F11 = 68, - scancode::f12, // SDL_SCANCODE_F12 = 69, - scancode::print_screen, // SDL_SCANCODE_PRINTSCREEN = 70, - scancode::scroll_lock, // SDL_SCANCODE_SCROLLLOCK = 71, - scancode::pause, // SDL_SCANCODE_PAUSE = 72, - scancode::insert, // SDL_SCANCODE_INSERT = 73, - scancode::home, // SDL_SCANCODE_HOME = 74, - scancode::page_up, // SDL_SCANCODE_PAGEUP = 75, - scancode::del, // SDL_SCANCODE_DELETE = 76, - scancode::end, // SDL_SCANCODE_END = 77, - scancode::page_down, // SDL_SCANCODE_PAGEDOWN = 78, - scancode::right, // SDL_SCANCODE_RIGHT = 79, - scancode::left, // SDL_SCANCODE_LEFT = 80, - scancode::down, // SDL_SCANCODE_DOWN = 81, - scancode::up, // SDL_SCANCODE_UP = 82, - scancode::num_lock, // SDL_SCANCODE_NUMLOCKCLEAR = 83, - scancode::kp_slash, // SDL_SCANCODE_KP_DIVIDE = 84, - scancode::kp_asterisk, // SDL_SCANCODE_KP_MULTIPLY = 85, - scancode::kp_minus, // SDL_SCANCODE_KP_MINUS = 86, - scancode::kp_plus, // SDL_SCANCODE_KP_PLUS = 87, - scancode::kp_enter, // SDL_SCANCODE_KP_ENTER = 88, - scancode::kp_1, // SDL_SCANCODE_KP_1 = 89, - scancode::kp_2, // SDL_SCANCODE_KP_2 = 90, - scancode::kp_3, // SDL_SCANCODE_KP_3 = 91, - scancode::kp_4, // SDL_SCANCODE_KP_4 = 92, - scancode::kp_5, // SDL_SCANCODE_KP_5 = 93, - scancode::kp_6, // SDL_SCANCODE_KP_6 = 94, - scancode::kp_7, // SDL_SCANCODE_KP_7 = 95, - scancode::kp_8, // SDL_SCANCODE_KP_8 = 96, - scancode::kp_9, // SDL_SCANCODE_KP_9 = 97, - scancode::kp_0, // SDL_SCANCODE_KP_0 = 98, - scancode::kp_dot, // SDL_SCANCODE_KP_PERIOD = 99, - scancode::non_us_backslash, // SDL_SCANCODE_NONUSBACKSLASH = 100, - scancode::application, // SDL_SCANCODE_APPLICATION = 101, - scancode::power, // SDL_SCANCODE_POWER = 102, - scancode::kp_equal, // SDL_SCANCODE_KP_EQUALS = 103, - scancode::f13, // SDL_SCANCODE_F13 = 104, - scancode::f14, // SDL_SCANCODE_F14 = 105, - scancode::f15, // SDL_SCANCODE_F15 = 106, - scancode::f16, // SDL_SCANCODE_F16 = 107, - scancode::f17, // SDL_SCANCODE_F17 = 108, - scancode::f18, // SDL_SCANCODE_F18 = 109, - scancode::f19, // SDL_SCANCODE_F19 = 110, - scancode::f20, // SDL_SCANCODE_F20 = 111, - scancode::f21, // SDL_SCANCODE_F21 = 112, - scancode::f22, // SDL_SCANCODE_F22 = 113, - scancode::f23, // SDL_SCANCODE_F23 = 114, - scancode::f24, // SDL_SCANCODE_F24 = 115, - scancode::execute, // SDL_SCANCODE_EXECUTE = 116, - scancode::help, // SDL_SCANCODE_HELP = 117, - scancode::menu, // SDL_SCANCODE_MENU = 118, - scancode::select, // SDL_SCANCODE_SELECT = 119, - scancode::stop, // SDL_SCANCODE_STOP = 120, - scancode::again, // SDL_SCANCODE_AGAIN = 121, - scancode::undo, // SDL_SCANCODE_UNDO = 122, - scancode::cut, // SDL_SCANCODE_CUT = 123, - scancode::copy, // SDL_SCANCODE_COPY = 124, - scancode::paste, // SDL_SCANCODE_PASTE = 125, - scancode::find, // SDL_SCANCODE_FIND = 126, - scancode::mute, // SDL_SCANCODE_MUTE = 127, - scancode::volume_up, // SDL_SCANCODE_VOLUMEUP = 128, - scancode::volume_down, // SDL_SCANCODE_VOLUMEDOWN = 129, - scancode::locking_caps_lock, // Unassigned = 130 - scancode::locking_num_lock, // Unassigned = 131 - scancode::locking_scroll_lock, // Unassigned = 132 - scancode::kp_comma, // SDL_SCANCODE_KP_COMMA = 133, - scancode::kp_equal_as400, // SDL_SCANCODE_KP_EQUALSAS400 = 134, - scancode::international_1, // SDL_SCANCODE_INTERNATIONAL1 = 135, - scancode::international_2, // SDL_SCANCODE_INTERNATIONAL2 = 136, - scancode::international_3, // SDL_SCANCODE_INTERNATIONAL3 = 137, - scancode::international_4, // SDL_SCANCODE_INTERNATIONAL4 = 138, - scancode::international_5, // SDL_SCANCODE_INTERNATIONAL5 = 139, - scancode::international_6, // SDL_SCANCODE_INTERNATIONAL6 = 140, - scancode::international_7, // SDL_SCANCODE_INTERNATIONAL7 = 141, - scancode::international_8, // SDL_SCANCODE_INTERNATIONAL8 = 142, - scancode::international_9, // SDL_SCANCODE_INTERNATIONAL9 = 143, - scancode::lang_1, // SDL_SCANCODE_LANG1 = 144, - scancode::lang_2, // SDL_SCANCODE_LANG2 = 145, - scancode::lang_3, // SDL_SCANCODE_LANG3 = 146, - scancode::lang_4, // SDL_SCANCODE_LANG4 = 147, - scancode::lang_5, // SDL_SCANCODE_LANG5 = 148, - scancode::lang_6, // SDL_SCANCODE_LANG6 = 149, - scancode::lang_7, // SDL_SCANCODE_LANG7 = 150, - scancode::lang_8, // SDL_SCANCODE_LANG8 = 151, - scancode::lang_9, // SDL_SCANCODE_LANG9 = 152, - scancode::alt_erase, // SDL_SCANCODE_ALTERASE = 153, - scancode::sys_req, // SDL_SCANCODE_SYSREQ = 154, - scancode::cancel, // SDL_SCANCODE_CANCEL = 155, - scancode::clear, // SDL_SCANCODE_CLEAR = 156, - scancode::prior, // SDL_SCANCODE_PRIOR = 157, - scancode::return_2, // SDL_SCANCODE_RETURN2 = 158, - scancode::separator, // SDL_SCANCODE_SEPARATOR = 159, - scancode::_out, // SDL_SCANCODE_OUT = 160, - scancode::oper, // SDL_SCANCODE_OPER = 161, - scancode::clear_again, // SDL_SCANCODE_CLEARAGAIN = 162, - scancode::cr_sel, // SDL_SCANCODE_CRSEL = 163, - scancode::ex_sel, // SDL_SCANCODE_EXSEL = 164, - scancode::unknown, // Unassigned = 165 - scancode::unknown, // Unassigned = 166 - scancode::unknown, // Unassigned = 167 - scancode::unknown, // Unassigned = 168 - scancode::unknown, // Unassigned = 169 - scancode::unknown, // Unassigned = 170 - scancode::unknown, // Unassigned = 171 - scancode::unknown, // Unassigned = 172 - scancode::unknown, // Unassigned = 173 - scancode::unknown, // Unassigned = 174 - scancode::unknown, // Unassigned = 175 - scancode::kp_00, // SDL_SCANCODE_KP_00 = 176, - scancode::kp_000, // SDL_SCANCODE_KP_000 = 177, - scancode::thousands_separator, // SDL_SCANCODE_THOUSANDSSEPARATOR = 178, - scancode::decimal_separator, // SDL_SCANCODE_DECIMALSEPARATOR = 179, - scancode::currency_unit, // SDL_SCANCODE_CURRENCYUNIT = 180, - scancode::currency_sub_unit, // SDL_SCANCODE_CURRENCYSUBUNIT = 181, - scancode::kp_left_paren, // SDL_SCANCODE_KP_LEFTPAREN = 182, - scancode::kp_right_paren, // SDL_SCANCODE_KP_RIGHTPAREN = 183, - scancode::kp_left_brace, // SDL_SCANCODE_KP_LEFTBRACE = 184, - scancode::kp_right_brace, // SDL_SCANCODE_KP_RIGHTBRACE = 185, - scancode::kp_tab, // SDL_SCANCODE_KP_TAB = 186, - scancode::kp_backspace, // SDL_SCANCODE_KP_BACKSPACE = 187, - scancode::kp_a, // SDL_SCANCODE_KP_A = 188, - scancode::kp_b, // SDL_SCANCODE_KP_B = 189, - scancode::kp_c, // SDL_SCANCODE_KP_C = 190, - scancode::kp_d, // SDL_SCANCODE_KP_D = 191, - scancode::kp_e, // SDL_SCANCODE_KP_E = 192, - scancode::kp_f, // SDL_SCANCODE_KP_F = 193, - scancode::kp_xor, // SDL_SCANCODE_KP_XOR = 194, - scancode::kp_power, // SDL_SCANCODE_KP_POWER = 195, - scancode::kp_percent, // SDL_SCANCODE_KP_PERCENT = 196, - scancode::kp_less, // SDL_SCANCODE_KP_LESS = 197, - scancode::kp_greater, // SDL_SCANCODE_KP_GREATER = 198, - scancode::kp_ampersand, // SDL_SCANCODE_KP_AMPERSAND = 199, - scancode::kp_double_ampersand, // SDL_SCANCODE_KP_DBLAMPERSAND = 200, - scancode::kp_vertical_bar, // SDL_SCANCODE_KP_VERTICALBAR = 201, - scancode::kp_double_vertical_bar, // SDL_SCANCODE_KP_DBLVERTICALBAR = 202, - scancode::kp_colon, // SDL_SCANCODE_KP_COLON = 203, - scancode::kp_hash, // SDL_SCANCODE_KP_HASH = 204, - scancode::kp_space, // SDL_SCANCODE_KP_SPACE = 205, - scancode::kp_at, // SDL_SCANCODE_KP_AT = 206, - scancode::kp_exclam, // SDL_SCANCODE_KP_EXCLAM = 207, - scancode::kp_mem_store, // SDL_SCANCODE_KP_MEMSTORE = 208, - scancode::kp_mem_recall, // SDL_SCANCODE_KP_MEMRECALL = 209, - scancode::kp_mem_clear, // SDL_SCANCODE_KP_MEMCLEAR = 210, - scancode::kp_mem_add, // SDL_SCANCODE_KP_MEMADD = 211, - scancode::kp_mem_subtract, // SDL_SCANCODE_KP_MEMSUBTRACT = 212, - scancode::kp_mem_multiply, // SDL_SCANCODE_KP_MEMMULTIPLY = 213, - scancode::kp_mem_divide, // SDL_SCANCODE_KP_MEMDIVIDE = 214, - scancode::kp_plus_minus, // SDL_SCANCODE_KP_PLUSMINUS = 215, - scancode::kp_clear, // SDL_SCANCODE_KP_CLEAR = 216, - scancode::kp_clear_entry, // SDL_SCANCODE_KP_CLEARENTRY = 217, - scancode::kp_binary, // SDL_SCANCODE_KP_BINARY = 218, - scancode::kp_octal, // SDL_SCANCODE_KP_OCTAL = 219, - scancode::kp_decimal, // SDL_SCANCODE_KP_DECIMAL = 220, - scancode::kp_hexadecimal, // SDL_SCANCODE_KP_HEXADECIMAL = 221, - scancode::unknown, // Unassigned = 222 - scancode::unknown, // Unassigned = 223 - scancode::left_ctrl, // SDL_SCANCODE_LCTRL = 224, - scancode::left_shift, // SDL_SCANCODE_LSHIFT = 225, - scancode::left_alt, // SDL_SCANCODE_LALT = 226, - scancode::left_gui, // SDL_SCANCODE_LGUI = 227, - scancode::right_ctrl, // SDL_SCANCODE_RCTRL = 228, - scancode::right_shift, // SDL_SCANCODE_RSHIFT = 229, - scancode::right_alt, // SDL_SCANCODE_RALT = 230, - scancode::right_gui, // SDL_SCANCODE_RGUI = 231, - scancode::unknown, // Unassigned = 232 - scancode::unknown, // Unassigned = 233 - scancode::unknown, // Unassigned = 234 - scancode::unknown, // Unassigned = 235 - scancode::unknown, // Unassigned = 236 - scancode::unknown, // Unassigned = 237 - scancode::unknown, // Unassigned = 238 - scancode::unknown, // Unassigned = 239 - scancode::unknown, // Unassigned = 240 - scancode::unknown, // Unassigned = 241 - scancode::unknown, // Unassigned = 242 - scancode::unknown, // Unassigned = 243 - scancode::unknown, // Unassigned = 244 - scancode::unknown, // Unassigned = 245 - scancode::unknown, // Unassigned = 246 - scancode::unknown, // Unassigned = 247 - scancode::unknown, // Unassigned = 248 - scancode::unknown, // Unassigned = 249 - scancode::unknown, // Unassigned = 250 - scancode::unknown, // Unassigned = 251 - scancode::unknown, // Unassigned = 252 - scancode::unknown, // Unassigned = 253 - scancode::unknown, // Unassigned = 254 - scancode::unknown, // Unassigned = 255 - scancode::unknown, // Unassigned = 256 - scancode::mode, // SDL_SCANCODE_MODE = 257, - scancode::audio_next, // SDL_SCANCODE_AUDIONEXT = 258, - scancode::audio_prev, // SDL_SCANCODE_AUDIOPREV = 259, - scancode::audio_stop, // SDL_SCANCODE_AUDIOSTOP = 260, - scancode::audio_play, // SDL_SCANCODE_AUDIOPLAY = 261, - scancode::audio_mute, // SDL_SCANCODE_AUDIOMUTE = 262, - scancode::media_select, // SDL_SCANCODE_MEDIASELECT = 263, - scancode::www, // SDL_SCANCODE_WWW = 264, - scancode::mail, // SDL_SCANCODE_MAIL = 265, - scancode::calculator, // SDL_SCANCODE_CALCULATOR = 266, - scancode::computer, // SDL_SCANCODE_COMPUTER = 267, - scancode::ac_search, // SDL_SCANCODE_AC_SEARCH = 268, - scancode::ac_home, // SDL_SCANCODE_AC_HOME = 269, - scancode::ac_back, // SDL_SCANCODE_AC_BACK = 270, - scancode::ac_forward, // SDL_SCANCODE_AC_FORWARD = 271, - scancode::ac_stop, // SDL_SCANCODE_AC_STOP = 272, - scancode::ac_refresh, // SDL_SCANCODE_AC_REFRESH = 273, - scancode::ac_bookmarks, // SDL_SCANCODE_AC_BOOKMARKS = 274, - scancode::brightness_down, // SDL_SCANCODE_BRIGHTNESSDOWN = 275, - scancode::brightness_up, // SDL_SCANCODE_BRIGHTNESSUP = 276, - scancode::display_switch, // SDL_SCANCODE_DISPLAYSWITCH = 277, - scancode::kbd_illum_toggle, // SDL_SCANCODE_KBDILLUMTOGGLE = 278, - scancode::kbd_illum_down, // SDL_SCANCODE_KBDILLUMDOWN = 279, - scancode::kbd_illum_up, // SDL_SCANCODE_KBDILLUMUP = 280, - scancode::eject, // SDL_SCANCODE_EJECT = 281, - scancode::sleep, // SDL_SCANCODE_SLEEP = 282, - scancode::app_1, // SDL_SCANCODE_APP1 = 283, - scancode::app_2, // SDL_SCANCODE_APP2 = 284, - scancode::audio_rewind, // SDL_SCANCODE_AUDIOREWIND = 285, - scancode::audio_fast_forward, // SDL_SCANCODE_AUDIOFASTFORWARD = 286, -}; - -} // namespace input diff --git a/src/main.cpp b/src/main.cpp index cb44012..ac6ba1c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,13 +17,133 @@ * along with Antkeeper source code. If not, see . */ +#include "config.hpp" +#include "debug/console.hpp" +#include "debug/log.hpp" #include "game/context.hpp" #include "game/state/boot.hpp" +#include "utility/ansi.hpp" +#include "utility/paths.hpp" +#include #include +#include #include +#include int main(int argc, char* argv[]) { + // Set up logging to standard output on debug builds + #if !defined(NDEBUG) + + // Enable VT100 sequences for colored text + debug::console::enable_vt100(); + + // Subscribe log to cout function to message logged events + auto log_to_cout_subscription = debug::log::default_logger().get_message_logged_channel().subscribe + ( + [&](const auto& event) + { + static const char* severities[] = + { + "trace", + "debug", + "info", + "warning", + "error", + "fatal" + }; + + static const std::string colors[] = + { + std::format("{}", ansi::fg_white), + std::format("{}", ansi::fg_bright_blue), + std::format("{}", ansi::fg_bright_green), + std::format("{}", ansi::fg_yellow), + std::format("{}", ansi::fg_red), + std::format("{}{}", ansi::fg_white, ansi::bg_bright_red) + }; + + std::osyncstream(std::cout) << std::format + ( + "{}:{}:{}: {}{}: {}{}\n", + std::filesystem::path(event.location.file_name()).filename().string(), + event.location.line(), + event.location.column(), + colors[static_cast(event.severity)], + severities[static_cast(event.severity)], + event.message, + ansi::reset + ); + } + ); + #endif + + // Determine path to log archive, then construct path if it doesn't exist + const std::filesystem::path log_archive_path = get_config_path(config::application_name) / "logs"; + const bool log_archive_created = std::filesystem::create_directories(log_archive_path); + + // If log archive already existed + if (!log_archive_created) + { + // Detect archived logs + std::set log_archive; + for (const auto& entry: std::filesystem::directory_iterator{log_archive_path}) + { + if (entry.is_regular_file() && + entry.path().extension() == ".log") + { + log_archive.emplace(entry.path()); + } + } + + // Delete expired logs + if (!log_archive.empty()) + { + for (std::size_t i = log_archive.size() + 1; i > config::debug_log_archive_capacity; --i) + { + std::filesystem::remove(*log_archive.begin()); + log_archive.erase(log_archive.begin()); + } + } + } + + // Set up logging to file + std::shared_ptr log_to_file_subscription; + if (config::debug_log_archive_capacity) + { + // Determine log filename + const auto time = std::chrono::floor(std::chrono::system_clock::now()); + const std::string log_filename = std::format("{0}-{1:%Y%m%d}T{1:%H%M%S}Z.log", config::application_name, time); + + // Open log file + std::filesystem::path log_filepath = log_archive_path / log_filename; + auto log_filestream = std::make_shared(log_filepath); + + // Write log file header + (*log_filestream) << "time\tfile\tline\tcolumn\tseverity\tmessage"; + + // Subscribe log to file function to message logged events + log_to_file_subscription = debug::log::default_logger().get_message_logged_channel().subscribe + ( + [log_filestream](const auto& event) + { + std::osyncstream(*log_filestream) << std::format + ( + "\n{0:%Y%m%d}T{0:%H%M%S}Z\t{1}\t{2}\t{3}\t{4}\t{5}", + std::chrono::floor(event.time), + std::filesystem::path(event.location.file_name()).filename().string(), + event.location.line(), + event.location.column(), + static_cast(event.severity), + event.message + ); + } + ); + } + + // Log application name and version string + debug::log::info("{} v{}", config::application_name, config::application_version_string); + try { // Allocate game context @@ -34,7 +154,7 @@ int main(int argc, char* argv[]) } catch (const std::exception& e) { - std::cerr << "Unhandled exception: \"" << e.what() << "\"" << std::endl; + debug::log::fatal("Unhandled exception: {}", e.what()); return EXIT_FAILURE; } diff --git a/src/math/map.hpp b/src/math/map.hpp index a84255f..6a54782 100644 --- a/src/math/map.hpp +++ b/src/math/map.hpp @@ -30,10 +30,11 @@ namespace math { * @param from_max End of the first range. * @param to_min Start of the second range. * @param to_max End of the second range. + * * @return Unclamped remapped value. */ template -T map(T x, T from_min, T from_max, T to_min, T to_max) +constexpr T map(T x, T from_min, T from_max, T to_min, T to_max) noexcept { return to_min + (x - from_min) * (to_max - to_min) / (from_max - from_min); } diff --git a/src/render/passes/material-pass.cpp b/src/render/passes/material-pass.cpp index f47252c..0721909 100644 --- a/src/render/passes/material-pass.cpp +++ b/src/render/passes/material-pass.cpp @@ -589,11 +589,6 @@ const material_pass::parameter_set* material_pass::load_parameter_set(const gl:: return parameters; } -void material_pass::handle_event(const mouse_moved_event& event) -{ - mouse_position = {static_cast(event.x), static_cast(event.y)}; -} - bool operation_compare(const render::operation& a, const render::operation& b) { if (!a.material) diff --git a/src/render/passes/material-pass.hpp b/src/render/passes/material-pass.hpp index ba0f6b0..0ff4ea7 100644 --- a/src/render/passes/material-pass.hpp +++ b/src/render/passes/material-pass.hpp @@ -23,8 +23,6 @@ #include "render/pass.hpp" #include "render/material.hpp" #include "utility/fundamental-types.hpp" -#include "event/event-handler.hpp" -#include "event/input-events.hpp" #include "gl/shader-program.hpp" #include "gl/shader-input.hpp" #include "gl/texture-2d.hpp" @@ -37,8 +35,7 @@ namespace render { /** * Renders scene objects using their material-specified shaders and properties. */ -class material_pass: public pass, - public event_handler +class material_pass: public pass { public: material_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); @@ -49,8 +46,6 @@ public: void set_fallback_material(const material* fallback); private: - virtual void handle_event(const mouse_moved_event& event); - /** * Sets of known shader input parameters. Each time a new shader is encountered, a parameter set will be created and its inputs connected to the shader program. A null input indiciates that the shader doesn't have that parameter. */ diff --git a/src/render/passes/shadow-map-pass.cpp b/src/render/passes/shadow-map-pass.cpp index a0df0ee..dfedde3 100644 --- a/src/render/passes/shadow-map-pass.cpp +++ b/src/render/passes/shadow-map-pass.cpp @@ -39,7 +39,6 @@ #include "math/projection.hpp" #include #include -#include namespace render { diff --git a/src/render/passes/sky-pass.cpp b/src/render/passes/sky-pass.cpp index 5a0f5b9..7af5638 100644 --- a/src/render/passes/sky-pass.cpp +++ b/src/render/passes/sky-pass.cpp @@ -46,7 +46,6 @@ #include #include #include -#include namespace render { @@ -664,9 +663,4 @@ void sky_pass::set_transmittance_lut_resolution(std::uint16_t width, std::uint16 render_transmittance_lut = true; } -void sky_pass::handle_event(const mouse_moved_event& event) -{ - mouse_position = {static_cast(event.x), static_cast(event.y)}; -} - } // namespace render diff --git a/src/render/passes/sky-pass.hpp b/src/render/passes/sky-pass.hpp index 674f867..f3ea421 100644 --- a/src/render/passes/sky-pass.hpp +++ b/src/render/passes/sky-pass.hpp @@ -23,8 +23,6 @@ #include "render/pass.hpp" #include "render/shader-template.hpp" #include "utility/fundamental-types.hpp" -#include "event/event-handler.hpp" -#include "event/input-events.hpp" #include "animation/tween.hpp" #include "math/quaternion.hpp" #include "gl/shader-program.hpp" @@ -46,8 +44,7 @@ class model; /** * */ -class sky_pass: public pass, - public event_handler +class sky_pass: public pass { public: sky_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); @@ -94,8 +91,6 @@ public: void set_transmittance_lut_resolution(std::uint16_t width, std::uint16_t height); private: - virtual void handle_event(const mouse_moved_event& event); - gl::vertex_buffer* quad_vbo; gl::vertex_array* quad_vao; diff --git a/src/resources/behavior-tree-loader.cpp b/src/resources/behavior-tree-loader.cpp index 20656b3..c80ebec 100644 --- a/src/resources/behavior-tree-loader.cpp +++ b/src/resources/behavior-tree-loader.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include diff --git a/src/resources/image-loader.cpp b/src/resources/image-loader.cpp index 1865268..51df910 100644 --- a/src/resources/image-loader.cpp +++ b/src/resources/image-loader.cpp @@ -134,9 +134,6 @@ image* resource_loader::load(resource_manager* resource_manager, PHYSFS_F { // Load all other formats with stb_image - // Determine if image is in an HDR format - bool hdr = (stbi_is_hdr_from_memory(buffer, size) != 0); - // Set vertical flip on load in order to upload pixels correctly to OpenGL stbi_set_flip_vertically_on_load(true); @@ -145,14 +142,7 @@ image* resource_loader::load(resource_manager* resource_manager, PHYSFS_F int width = 0; int height = 0; int channels = 0; - if (hdr) - { - pixels = stbi_loadf_from_memory(buffer, size, &width, &height, &channels, 0); - } - else - { - pixels = stbi_load_from_memory(buffer, size, &width, &height, &channels, 0); - } + pixels = stbi_load_from_memory(buffer, size, &width, &height, &channels, 0); // Free file buffer delete[] buffer; @@ -164,7 +154,7 @@ image* resource_loader::load(resource_manager* resource_manager, PHYSFS_F } // Create image - std::size_t component_size = (hdr) ? sizeof(float) : sizeof(unsigned char); + std::size_t component_size = sizeof(unsigned char); image = new ::image(); image->format(component_size, channels); image->resize(static_cast(width), static_cast(height)); diff --git a/src/resources/resource-loader.hpp b/src/resources/resource-loader.hpp index b71c609..1774ae2 100644 --- a/src/resources/resource-loader.hpp +++ b/src/resources/resource-loader.hpp @@ -17,8 +17,8 @@ * along with Antkeeper source code. If not, see . */ -#ifndef RESOURCE_LOADER_HPP -#define RESOURCE_LOADER_HPP +#ifndef ANTKEEPER_RESOURCES_RESOURCE_LOADER_HPP +#define ANTKEEPER_RESOURCES_RESOURCE_LOADER_HPP #include #include @@ -57,4 +57,4 @@ public: /// getline function for PhysicsFS file handles void physfs_getline(PHYSFS_File* file, std::string& line); -#endif // RESOURCE_LOADER_HPP +#endif // ANTKEEPER_RESOURCES_RESOURCE_LOADER_HPP diff --git a/src/resources/resource-manager.cpp b/src/resources/resource-manager.cpp index 7f85253..771dc58 100644 --- a/src/resources/resource-manager.cpp +++ b/src/resources/resource-manager.cpp @@ -19,55 +19,55 @@ #include "resources/resource-manager.hpp" -resource_manager::resource_manager(debug::logger* logger): - logger(logger) +resource_manager::resource_manager() { // Init PhysicsFS - logger->push_task("Initializing PhysicsFS"); + debug::log::trace("Initializing PhysicsFS..."); if (!PHYSFS_init(nullptr)) { - logger->error(std::string("PhysicsFS error: ") + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); - logger->pop_task(EXIT_FAILURE); + debug::log::error("Failed to initialize PhysicsFS: {}", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); } else { - logger->pop_task(EXIT_SUCCESS); + debug::log::trace("Initialized PhysicsFS"); } } resource_manager::~resource_manager() { + debug::log::trace("Deleting cached resources..."); + // Delete cached resources for (auto it = resource_cache.begin(); it != resource_cache.end(); ++it) { delete it->second; } + debug::log::trace("Deleted cached resources"); + // Deinit PhysicsFS - logger->push_task("Deinitializing PhysicsFS"); + debug::log::trace("Deinitializing PhysicsFS..."); if (!PHYSFS_deinit()) { - logger->error(std::string("PhysicsFS error: ") + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); - logger->pop_task(EXIT_FAILURE); + debug::log::error("Failed to deinitialize PhysicsFS: {}", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); } else { - logger->pop_task(EXIT_SUCCESS); + debug::log::trace("Deinitialized PhysicsFS"); } } bool resource_manager::mount(const std::filesystem::path& path) { - logger->push_task("Mounting path \"" + path.string() + "\""); + debug::log::trace("Mounting path \"{}\"...", path.string()); if (!PHYSFS_mount(path.string().c_str(), nullptr, 1)) { - logger->error(std::string("PhysicsFS error: ") + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); - logger->pop_task(EXIT_FAILURE); + debug::log::error("Failed to mount path \"{}\": {}", path.string(), PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); return false; } else { - logger->pop_task(EXIT_SUCCESS); + debug::log::trace("Mounted path \"{}\"", path.string()); return true; } } @@ -80,23 +80,17 @@ void resource_manager::unload(const std::filesystem::path& path) { // Decrement the resource handle reference count --it->second->reference_count; - + // Free the resource if the resource handle is unreferenced if (it->second->reference_count <= 0) { - if (logger) - { - logger->push_task("Unloading resource \"" + path.string() + "\""); - } + debug::log::trace("Unloading resource \"{}\"...", path.string()); delete it->second; - if (logger) - { - logger->pop_task(EXIT_SUCCESS); - } + debug::log::trace("Unloaded resource \"{}\"", path.string()); } - + // Remove resource from the cache resource_cache.erase(it); } @@ -106,4 +100,3 @@ void resource_manager::include(const std::filesystem::path& search_path) { search_paths.push_back(search_path); } - diff --git a/src/resources/resource-manager.hpp b/src/resources/resource-manager.hpp index 9e59a19..0621ddb 100644 --- a/src/resources/resource-manager.hpp +++ b/src/resources/resource-manager.hpp @@ -17,20 +17,18 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_RESOURCE_MANAGER_HPP -#define ANTKEEPER_RESOURCE_MANAGER_HPP +#ifndef ANTKEEPER_RESOURCES_RESOURCE_MANAGER_HPP +#define ANTKEEPER_RESOURCES_RESOURCE_MANAGER_HPP -#include "resource-handle.hpp" -#include "resource-loader.hpp" -#include "debug/logger.hpp" -#include +#include "resources/resource-handle.hpp" +#include "resources/resource-loader.hpp" +#include "debug/log.hpp" +#include #include #include +#include #include #include -#include -#include -#include /** * Loads resources. @@ -41,7 +39,7 @@ public: /** * Creates a resource manager. */ - resource_manager(debug::logger* logger); + resource_manager(); /** * Destroys a resource manager and frees all of its resources. @@ -84,13 +82,9 @@ public: template void save(const T* resource, const std::filesystem::path& path); - entt::registry& get_archetype_registry(); - private: std::map resource_cache; std::list search_paths; - entt::registry archetype_registry; - debug::logger* logger; }; template @@ -100,28 +94,20 @@ T* resource_manager::load(const std::filesystem::path& path) auto it = resource_cache.find(path); if (it != resource_cache.end()) { - /* - if (logger) - { - logger->log("Fetched resource \"" + path.string() + "\""); - } - */ + //debug::log::trace("Fetched cached resource \"{}\"". path.string()); // Resource found resource_handle* resource = static_cast*>(it->second); - + // Increment resource handle reference count ++resource->reference_count; - + // Return resource data return resource->data; } - if (logger) - { - logger->push_task("Loading resource \"" + path.string() + "\""); - } - + debug::log::trace("Loading resource \"{}\"...", path.string()); + // Resource not cached, look for file in search paths T* data = nullptr; bool found = false; @@ -142,10 +128,10 @@ T* resource_manager::load(const std::filesystem::path& path) PHYSFS_File* file = PHYSFS_openRead(full_path.string().c_str()); if (!file) { - logger->error(std::string("PhysicsFS error: ") + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + debug::log::error("Failed to load resource \"{}\": {}", path.string(), PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); break; } - + // Load opened file try { @@ -153,13 +139,13 @@ T* resource_manager::load(const std::filesystem::path& path) } catch (const std::exception& e) { - logger->error("Failed to load resource: \"" + std::string(e.what()) + "\""); + debug::log::error("Failed to load resource \"{}\": {}", path.string(), e.what()); } // Close opened file if (!PHYSFS_close(file)) { - logger->error(std::string("PhysicsFS error: ") + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + debug::log::error("Failed to close resource file \"{}\": {}", path.string(), PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); } break; @@ -169,10 +155,9 @@ T* resource_manager::load(const std::filesystem::path& path) { if (!found) { - logger->error("File not found"); + debug::log::error("Failed to load resource \"{}\": file not found", path.string()); } - - logger->pop_task(EXIT_FAILURE); + return nullptr; } @@ -184,10 +169,7 @@ T* resource_manager::load(const std::filesystem::path& path) // Add resource to the cache resource_cache[path] = resource; - if (logger) - { - logger->pop_task(EXIT_SUCCESS); - } + debug::log::trace("Loaded resource \"{}\"", path.string()); return resource->data; } @@ -195,43 +177,32 @@ T* resource_manager::load(const std::filesystem::path& path) template void resource_manager::save(const T* resource, const std::filesystem::path& path) { - logger->push_task("Saving resource to \"" + path.string() + "\""); + debug::log::trace("Saving resource to \"{}\"", path.string()); // Open file for writing PHYSFS_File* file = PHYSFS_openWrite(path.string().c_str()); if (!file) { - logger->error(std::string("PhysicsFS error: ") + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); - logger->pop_task(EXIT_FAILURE); + debug::log::error("Failed to save resource to \"{}\": {}", path.string(), PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); return; } // Save to opened file - int status = EXIT_SUCCESS; try { resource_loader::save(this, file, path, resource); + debug::log::trace("Saved resource to \"{}\"", path.string()); } catch (const std::exception& e) { - logger->error("Failed to save resource: \"" + std::string(e.what()) + "\""); - status = EXIT_FAILURE; + debug::log::error("Failed to save resource to \"{}\": {}", e.what()); } // Close opened file if (!PHYSFS_close(file)) { - logger->error(std::string("PhysicsFS error: ") + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); - status = EXIT_FAILURE; + debug::log::error("Failed to close file \"{}\": {}", path.string(), PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); } - - logger->pop_task(status); -} - -inline entt::registry& resource_manager::get_archetype_registry() -{ - return archetype_registry; } -#endif // ANTKEEPER_RESOURCE_MANAGER_HPP - +#endif // ANTKEEPER_RESOURCES_RESOURCE_MANAGER_HPP diff --git a/src/ui/mouse-tracker.cpp b/src/ui/mouse-tracker.cpp deleted file mode 100644 index 2c0f3f6..0000000 --- a/src/ui/mouse-tracker.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "ui/mouse-tracker.hpp" - -namespace ui { - -mouse_tracker::mouse_tracker(): - mouse_moved_callback(nullptr), - mouse_button_pressed_callback(nullptr), - mouse_button_released_callback(nullptr), - mouse_wheel_scrolled_callback(nullptr) -{} - -void mouse_tracker::set_mouse_moved_callback(const std::function& callback) -{ - mouse_moved_callback = callback; -} - -void mouse_tracker::set_mouse_button_pressed_callback(const std::function& callback) -{ - mouse_button_pressed_callback = callback; -} - -void mouse_tracker::set_mouse_button_released_callback(const std::function& callback) -{ - mouse_button_released_callback = callback; -} - -void mouse_tracker::set_mouse_wheel_scrolled_callback(const std::function& callback) -{ - mouse_wheel_scrolled_callback = callback; -} - -void mouse_tracker::handle_event(const mouse_moved_event& event) -{ - if (mouse_moved_callback) - mouse_moved_callback(event); -} - -void mouse_tracker::handle_event(const mouse_button_pressed_event& event) -{ - if (mouse_button_pressed_callback) - mouse_button_pressed_callback(event); -} - -void mouse_tracker::handle_event(const mouse_button_released_event& event) -{ - if (mouse_button_released_callback) - mouse_button_released_callback(event); -} - -void mouse_tracker::handle_event(const mouse_wheel_scrolled_event& event) -{ - if (mouse_wheel_scrolled_callback) - mouse_wheel_scrolled_callback(event); -} - -} // namespace ui diff --git a/src/ui/mouse-tracker.hpp b/src/ui/mouse-tracker.hpp deleted file mode 100644 index 4c523f7..0000000 --- a/src/ui/mouse-tracker.hpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_UI_MOUSE_TRACKER_HPP -#define ANTKEEPER_UI_MOUSE_TRACKER_HPP - -#include "event/input-events.hpp" -#include "event/event-handler.hpp" -#include - -namespace ui { - -class mouse_tracker: - public event_handler, - public event_handler, - public event_handler, - public event_handler -{ -public: - mouse_tracker(); - virtual ~mouse_tracker() = default; - - void set_mouse_moved_callback(const std::function& callback); - void set_mouse_button_pressed_callback(const std::function& callback); - void set_mouse_button_released_callback(const std::function& callback); - void set_mouse_wheel_scrolled_callback(const std::function& callback); - -private: - virtual void handle_event(const mouse_moved_event& event); - virtual void handle_event(const mouse_button_pressed_event& event); - virtual void handle_event(const mouse_button_released_event& event); - virtual void handle_event(const mouse_wheel_scrolled_event& event); - - std::function mouse_moved_callback; - std::function mouse_button_pressed_callback; - std::function mouse_button_released_callback; - std::function mouse_wheel_scrolled_callback; -}; - -} // namespace ui - -#endif // ANTKEEPER_UI_MOUSE_TRACKER_HPP diff --git a/src/utility/ansi.hpp b/src/utility/ansi.hpp new file mode 100644 index 0000000..2140695 --- /dev/null +++ b/src/utility/ansi.hpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_UTILITY_ANSI_HPP +#define ANTKEEPER_UTILITY_ANSI_HPP + +/** + * ANSI escape sequences. + * + * @see https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences + */ +namespace ansi { + +const char* reset = "\33[0m"; +const char* bold = "\33[1m"; +const char* underline = "\33[4m"; +const char* invert = "\33[7m"; +const char* no_bold = "\33[22m"; +const char* no_underline = "\33[24m"; +const char* no_invert = "\33[24m"; +const char* fg_black = "\33[30m"; +const char* fg_red = "\33[31m"; +const char* fg_green = "\33[32m"; +const char* fg_yellow = "\33[33m"; +const char* fg_blue = "\33[34m"; +const char* fg_magenta = "\33[35m"; +const char* fg_cyan = "\33[36m"; +const char* fg_white = "\33[37m"; +const char* fg_extended = "\33[38m"; +const char* fg_reset = "\33[39m"; +const char* bg_black = "\33[40m"; +const char* bg_red = "\33[41m"; +const char* bg_green = "\33[42m"; +const char* bg_yellow = "\33[43m"; +const char* bg_blue = "\33[44m"; +const char* bg_magenta = "\33[45m"; +const char* bg_cyan = "\33[46m"; +const char* bg_white = "\33[47m"; +const char* bg_extended = "\33[48m"; +const char* bg_reset = "\33[49m"; +const char* fg_bright_black = "\33[30;1m"; +const char* fg_bright_red = "\33[31;1m"; +const char* fg_bright_green = "\33[32;1m"; +const char* fg_bright_yellow = "\33[33;1m"; +const char* fg_bright_blue = "\33[34;1m"; +const char* fg_bright_magenta = "\33[35;1m"; +const char* fg_bright_cyan = "\33[36;1m"; +const char* fg_bright_white = "\33[37;1m"; +const char* bg_bright_black = "\33[40;1m"; +const char* bg_bright_red = "\33[41;1m"; +const char* bg_bright_green = "\33[42;1m"; +const char* bg_bright_yellow = "\33[43;1m"; +const char* bg_bright_blue = "\33[44;1m"; +const char* bg_bright_magenta = "\33[45;1m"; +const char* bg_bright_cyan = "\33[46;1m"; +const char* bg_bright_white = "\33[47;1m"; + +} // namespace ansi + +#endif // ANTKEEPER_UTILITY_ANSI_HPP diff --git a/src/utility/timestamp.hpp b/src/utility/hash.hpp similarity index 77% rename from src/utility/timestamp.hpp rename to src/utility/hash.hpp index 06fca93..9ffba42 100644 --- a/src/utility/timestamp.hpp +++ b/src/utility/hash.hpp @@ -17,14 +17,15 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_TIMESTAMP_HPP -#define ANTKEEPER_TIMESTAMP_HPP - -#include +#ifndef ANTKEEPER_UTILITY_HASH_HPP +#define ANTKEEPER_UTILITY_HASH_HPP /** - * Returns a string containing the current time, formatted as "YYYYMMDD-HHMMSS-mmm". + * Hash functions. */ -std::string timestamp(); +namespace hash {} + +#include "utility/hash/fnv1a.hpp" +#include "utility/hash/literals.hpp" -#endif // ANTKEEPER_TIMESTAMP_HPP +#endif // ANTKEEPER_UTILITY_HASH_HPP diff --git a/src/utility/hash/fnv1a.hpp b/src/utility/hash/fnv1a.hpp new file mode 100644 index 0000000..5112e65 --- /dev/null +++ b/src/utility/hash/fnv1a.hpp @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_UTILITY_HASH_FNV1A_HPP +#define ANTKEEPER_UTILITY_HASH_FNV1A_HPP + +#include +#include +#include + +namespace hash { + +/** + * FNV-1a hash function. + * + * @tparam HashT Unsigned integral hash type. + * @tparam CharT String character type. + * + * @param string String to hash. + * @param length Number of characters in @p string. + * @param offset FNV offset basis value. + * @param prime FNV prime value. + * + * @return Hash value. + * + * @see https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function + */ +template +[[nodiscard]] constexpr HashT fnv1a(const CharT* string, std::size_t length, HashT offset, HashT prime) noexcept +{ + for (std::size_t i = 0; i < length; ++i) + { + if constexpr (sizeof(CharT) == 1) + { + offset ^= static_cast(*string); + offset *= prime; + } + else + { + /// @TODO `reinterpret_cast` is not supported in consteval. C++23 has `if consteval` which can selectively enable reinterpret_cast at runtime, and extract bytes manually at compile-time. + for (std::size_t j = 0; j < sizeof(CharT); ++j) + { + offset ^= static_cast(((*string) >> (j * 8)) & 255); + offset *= prime; + } + } + + ++string; + } + + return offset; +} + +/** + * 32-bit FNV-1a hash function. + * + * @param string String to hash. + * @param length Number of characters in @p string. + * + * @return 32-bit hash value. + */ +template +[[nodiscard]] constexpr std::uint32_t fnv1a32(const CharT* string, std::size_t length) noexcept +{ + // 32-bit FNV offset basis value. + constexpr std::uint32_t offset = 2166136261; + + // 32-bit FNV prime value. + constexpr std::uint32_t prime = 16777619; + + return fnv1a(string, length, offset, prime); +} + +/** + * 64-bit FNV-1a hash function. + * + * @param string String to hash. + * @param length Number of characters in @p string. + * + * @return 64-bit hash value. + */ +template +[[nodiscard]] constexpr std::uint64_t fnv1a64(const CharT* string, std::size_t length) noexcept +{ + // 64-bit FNV offset basis value. + constexpr std::uint64_t offset = 14695981039346656037ULL; + + // 64-bit FNV prime value. + constexpr std::uint64_t prime = 1099511628211ULL; + + return fnv1a(string, length, offset, prime); +} + +namespace literals { + +/** + * Hashes a string at compile-time using the 32-bit FNV-1a hash function. + * + * @param string String to hash. + * @param length Number of characters in @p string. + * + * @return 32-bit hash value. + */ +/// @{ +[[nodiscard]] consteval std::uint32_t operator"" _fnv1a32(const char* string, std::size_t length) noexcept +{ + return hash::fnv1a32(string, length); +} + +[[nodiscard]] consteval std::uint32_t operator"" _fnv1a32(const wchar_t* string, std::size_t length) noexcept +{ + return hash::fnv1a32(string, length); +} + +[[nodiscard]] consteval std::uint32_t operator"" _fnv1a32(const char8_t* string, std::size_t length) noexcept +{ + return hash::fnv1a32(string, length); +} + +[[nodiscard]] consteval std::uint32_t operator"" _fnv1a32(const char16_t* string, std::size_t length) noexcept +{ + return hash::fnv1a32(string, length); +} + +[[nodiscard]] consteval std::uint32_t operator"" _fnv1a32(const char32_t* string, std::size_t length) noexcept +{ + return hash::fnv1a32(string, length); +} +/// @} + +/** + * Hashes a string at compile-time using the 64-bit FNV-1a hash function. + * + * @param string String to hash. + * @param length Number of characters in @p string. + * + * @return 64-bit hash value. + */ +/// @{ +[[nodiscard]] consteval std::uint64_t operator"" _fnv1a64(const char* string, std::size_t length) noexcept +{ + return hash::fnv1a64(string, length); +} + +[[nodiscard]] consteval std::uint64_t operator"" _fnv1a64(const wchar_t* string, std::size_t length) noexcept +{ + return hash::fnv1a64(string, length); +} + +[[nodiscard]] consteval std::uint64_t operator"" _fnv1a64(const char8_t* string, std::size_t length) noexcept +{ + return hash::fnv1a64(string, length); +} + +[[nodiscard]] consteval std::uint64_t operator"" _fnv1a64(const char16_t* string, std::size_t length) noexcept +{ + return hash::fnv1a64(string, length); +} + +[[nodiscard]] consteval std::uint64_t operator"" _fnv1a64(const char32_t* string, std::size_t length) noexcept +{ + return hash::fnv1a64(string, length); +} +/// @} + +} // namespace literals + +} // namespace hash + +#endif // ANTKEEPER_UTILITY_HASH_FNV1A_HPP diff --git a/src/input/sdl-scancode-table.hpp b/src/utility/hash/literals.hpp similarity index 75% rename from src/input/sdl-scancode-table.hpp rename to src/utility/hash/literals.hpp index 93f420a..cb76f65 100644 --- a/src/input/sdl-scancode-table.hpp +++ b/src/utility/hash/literals.hpp @@ -17,16 +17,16 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_INPUT_SDL_SCANCODE_TABLE_HPP -#define ANTKEEPER_INPUT_SDL_SCANCODE_TABLE_HPP +#ifndef ANTKEEPER_UTILITY_HASH_LITERALS_HPP +#define ANTKEEPER_UTILITY_HASH_LITERALS_HPP -namespace input { +namespace hash { -enum class scancode; - -extern const scancode sdl_scancode_table[287]; - -} // namespace input +/** + * User-defined literals for compile-time string hashing. + */ +namespace literals {} -#endif // ANTKEEPER_INPUT_SDL_SCANCODE_TABLE_HPP +} // namespace hash +#endif // ANTKEEPER_UTILITY_HASH_LITERALS_HPP diff --git a/src/utility/paths.hpp b/src/utility/paths.hpp index 2e0784c..d0b989c 100644 --- a/src/utility/paths.hpp +++ b/src/utility/paths.hpp @@ -28,7 +28,7 @@ * * @return Path to the application's executable. */ -std::filesystem::path get_executable_path(); +[[nodiscard]] std::filesystem::path get_executable_path(); /** * Returns the absolute path to the directory containing application data. @@ -39,7 +39,7 @@ std::filesystem::path get_executable_path(); * @param application_name Name of the application. * @return Path to the application's data directory. */ -std::filesystem::path get_data_path(const std::string& application_name); +[[nodiscard]] std::filesystem::path get_data_path(const std::string& application_name); /** * Returns the absolute path to the directory containing user-specific application data. @@ -50,7 +50,7 @@ std::filesystem::path get_data_path(const std::string& application_name); * @param application_name Name of the application. * @return Path to the application's config directory. */ -std::filesystem::path get_config_path(const std::string& application_name); +[[nodiscard]] std::filesystem::path get_config_path(const std::string& application_name); #endif // ANTKEEPER_PATHS_HPP diff --git a/src/utility/timestamp.cpp b/src/utility/timestamp.cpp deleted file mode 100644 index faf6af8..0000000 --- a/src/utility/timestamp.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "timestamp.hpp" -#include -#include -#include -#include - -std::string timestamp() -{ - const char* time_format = "%y%m%d-%H%M%S-"; - - auto now = std::chrono::system_clock::now(); - std::time_t tt = std::chrono::system_clock::to_time_t(now); - std::size_t ms = (std::chrono::duration_cast(now.time_since_epoch()) % 1000).count(); - - #if defined(_WIN32) - struct std::tm timeinfo; - localtime_s(&timeinfo, &tt); - std::stringstream stream; - stream << std::put_time(&timeinfo, time_format); - stream << std::setfill('0') << std::setw(3) << ms; - #else - struct std::tm* timeinfo = localtime(&tt); - std::stringstream stream; - stream << std::put_time(timeinfo, time_format); - stream << std::setfill('0') << std::setw(3) << ms; - #endif - - return stream.str(); -} - diff --git a/src/utility/type-id.hpp b/src/utility/type-id.hpp new file mode 100644 index 0000000..5d53dcd --- /dev/null +++ b/src/utility/type-id.hpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_UTILITY_TYPE_ID_HPP +#define ANTKEEPER_UTILITY_TYPE_ID_HPP + +/** + * Provides unique type IDs at compile-time via the templated addresses of this function. + * + * @tparam T Type. + */ +template +void type_id_generator() noexcept {} + +/// Type ID type. +using type_id_t = void(*)(void); + +/** + * Compile-time constant type ID. + * + * @tparam T Type. + */ +template +constexpr type_id_t type_id = &type_id_generator; + +#endif // ANTKEEPER_UTILITY_TYPE_ID_HPP diff --git a/src/event/signal.cpp b/src/utility/uuid.cpp similarity index 65% rename from src/event/signal.cpp rename to src/utility/uuid.cpp index bcad1c9..18bf4fc 100644 --- a/src/event/signal.cpp +++ b/src/utility/uuid.cpp @@ -17,31 +17,27 @@ * along with Antkeeper source code. If not, see . */ -#include "event/signal.hpp" +#include "utility/uuid.hpp" +#include -//namespace event { - -connection::connection(std::weak_ptr handler, disconnector_type disconnector): - handler(handler), - disconnector(disconnector) -{} - -connection::~connection() -{ - disconnect(); -} - -bool connection::connected() const noexcept +std::string uuid::string() const { - return !handler.expired(); -} - -void connection::disconnect() -{ - if (connected()) + static const char* hex = "0123456789abcdef"; + + std::string str(32, '0'); + + char* c = str.data(); + for (std::byte byte: data) { - disconnector(handler); + *(c++) = hex[static_cast(byte) >> 4]; + *(c++) = hex[static_cast(byte) & 15]; } + + return str; } -//} // namespace event +std::ostream& operator<<(std::ostream& os, const uuid& id) +{ + os << id.string(); + return os; +} diff --git a/src/utility/uuid.hpp b/src/utility/uuid.hpp new file mode 100644 index 0000000..af7ba50 --- /dev/null +++ b/src/utility/uuid.hpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_UTILITY_UUID_HPP +#define ANTKEEPER_UTILITY_UUID_HPP + +#include +#include +#include +#include + +/** + * 128-bit universally unique identifier (UUID). + */ +struct uuid +{ + /// Returns a string representation of the UUID. + [[nodiscard]] std::string string() const; + + /// UUID data. + std::array data; +}; + +/** + * Writes a UUID to an output stream. + * + * @param os Output stream. + * @param id UUID. + * + * @return Output stream. + */ +std::ostream& operator<<(std::ostream& os, const uuid& id); + +#endif // ANTKEEPER_UTILITY_UUID_HPP