@ -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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#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 | |||||
@ -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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#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<void()>(std::bind(&debug::cli::interpret, cli, command))}); | |||||
return std::string("command \"" + command + "\" will execute in " + std::to_string(t) + " seconds"); | |||||
} | |||||
} // namespace cc | |||||
} // namespace debug |
@ -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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#include "debug/console.hpp" | |||||
#if defined(_WIN32) | |||||
#define WIN32_LEAN_AND_MEAN | |||||
#include <windows.h> | |||||
#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 |
@ -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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#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 |
@ -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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#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<std::source_location>(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<std::source_location>(location)); | |||||
} | |||||
else | |||||
{ | |||||
message += "failure"; | |||||
if (!description.empty()) | |||||
{ | |||||
message += "(" + description + ")"; | |||||
} | |||||
default_logger().log(std::move(message), message_severity::error, std::forward<std::source_location>(location)); | |||||
} | |||||
} | |||||
} // namespace log | |||||
} // namespace debug |
@ -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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#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 <source_location> | |||||
#include <string> | |||||
#include <format> | |||||
// 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 <message_severity Severity, class... Args> | |||||
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<std::underlying_type_t<message_severity>>(Severity)) | |||||
{ | |||||
default_logger().log(std::vformat(format, std::make_format_args(std::forward<Args>(args)...)), Severity, std::forward<std::source_location>(location)); | |||||
} | |||||
} | |||||
}; | |||||
// Use class template argument deduction (CTAD) to capture source location as a default argument following variadic format arguments. | |||||
template <message_severity Severity, class... Args> | |||||
message(std::string_view, Args&&...) -> message<Severity, Args...>; | |||||
#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 <class... Args> | |||||
using trace = message<message_severity::trace, Args...>; | |||||
#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 <class... Args> | |||||
using debug = message<message_severity::debug, Args...>; | |||||
#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 <class... Args> | |||||
using info = message<message_severity::info, Args...>; | |||||
#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 <class... Args> | |||||
using warning = message<message_severity::warning, Args...>; | |||||
#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 <class... Args> | |||||
using error = message<message_severity::error, Args...>; | |||||
#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 <class... Args> | |||||
using fatal = message<message_severity::fatal, Args...>; | |||||
#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 |
@ -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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#ifndef ANTKEEPER_DEBUG_LOG_EVENT_HPP | |||||
#define ANTKEEPER_DEBUG_LOG_EVENT_HPP | |||||
#include "debug/log/message-severity.hpp" | |||||
#include <chrono> | |||||
#include <source_location> | |||||
#include <string> | |||||
#include <thread> | |||||
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<std::chrono::system_clock> 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 |
@ -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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#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 <source_location> | |||||
#include <string> | |||||
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<event::message_logged>& get_message_logged_channel() noexcept | |||||
{ | |||||
return message_logged_publisher.channel(); | |||||
} | |||||
private: | |||||
::event::publisher<event::message_logged> message_logged_publisher; | |||||
}; | |||||
} // namespace log | |||||
} // namespace debug | |||||
#endif // ANTKEEPER_DEBUG_LOG_LOGGER_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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#ifndef ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_HPP | |||||
#define ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_HPP | |||||
#include <cstdint> | |||||
/// 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 |
@ -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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#include "logger.hpp" | |||||
#include "utility/timestamp.hpp" | |||||
#include <iostream> | |||||
#if defined(_WIN32) | |||||
#define WIN32_LEAN_AND_MEAN | |||||
#include <windows.h> | |||||
#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<std::mutex> 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 |
@ -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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#ifndef ANTKEEPER_DEBUG_LOGGER_HPP | |||||
#define ANTKEEPER_DEBUG_LOGGER_HPP | |||||
#include <list> | |||||
#include <ostream> | |||||
#include <stack> | |||||
#include <string> | |||||
#include <mutex> | |||||
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<std::string> 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 | |||||
@ -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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#ifndef ANTKEEPER_EVENT_CHANNEL_HPP | |||||
#define ANTKEEPER_EVENT_CHANNEL_HPP | |||||
#include <functional> | |||||
#include <list> | |||||
#include <memory> | |||||
#include <utility> | |||||
#include "event/subscriber.hpp" | |||||
#include "event/subscription.hpp" | |||||
#include "event/queue.hpp" | |||||
namespace event { | |||||
template <class T> | |||||
class publisher; | |||||
/** | |||||
* Channel through which messages are published. | |||||
* | |||||
* @tparam T Message type. | |||||
*/ | |||||
template <class T> | |||||
class channel | |||||
{ | |||||
public: | |||||
/// Message type. | |||||
typedef T message_type; | |||||
/// Subscriber function object type. | |||||
typedef subscriber<message_type> 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<subscription> subscribe(subscriber_type&& subscriber) | |||||
{ | |||||
// Construct shared pointer to subscriber function | |||||
std::shared_ptr<subscriber_type> shared_subscriber = std::make_shared<subscriber_type>(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<subscription> | |||||
( | |||||
std::static_pointer_cast<void>(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<subscription> subscribe(event::queue& queue) | |||||
{ | |||||
return subscribe | |||||
( | |||||
[&queue](const message_type& message) | |||||
{ | |||||
queue.enqueue<message_type>(message); | |||||
} | |||||
); | |||||
} | |||||
private: | |||||
friend class publisher<T>; | |||||
/// List of subscribers. | |||||
std::list<std::shared_ptr<subscriber_type>> subscribers; | |||||
}; | |||||
} // namespace event | |||||
#endif // ANTKEEPER_EVENT_CHANNEL_HPP |
@ -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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#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<event_handler_base*>& 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(); | |||||
} | |||||
@ -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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#ifndef ANTKEEPER_EVENT_DISPATCHER_HPP | |||||
#define ANTKEEPER_EVENT_DISPATCHER_HPP | |||||
#include "event.hpp" | |||||
#include "event-handler.hpp" | |||||
#include <cstddef> | |||||
#include <list> | |||||
#include <map> | |||||
/** | |||||
* 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 <typename T> | |||||
void subscribe(event_handler<T>* handler); | |||||
/** | |||||
* Unsubscribes an event handler from event dispatches. | |||||
* | |||||
* @param handler Handler to unsubscribe. | |||||
*/ | |||||
template <typename T> | |||||
void unsubscribe(event_handler<T>* 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<std::tuple<std::size_t, event_handler_base*>> to_subscribe; | |||||
std::list<std::tuple<std::size_t, event_handler_base*>> to_unsubscribe; | |||||
std::map<std::size_t, std::list<event_handler_base*>> handler_map; | |||||
std::list<event_base*> queued_events; | |||||
std::multimap<double, event_base*> scheduled_events; | |||||
bool updating; | |||||
}; | |||||
template <typename T> | |||||
void event_dispatcher::subscribe(event_handler<T>* 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 <typename T> | |||||
void event_dispatcher::unsubscribe(event_handler<T>* 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<double, event_base*>(time, event.clone())); | |||||
} | |||||
#endif // ANTKEEPER_EVENT_DISPATCHER_HPP | |||||
@ -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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#ifndef ANTKEEPER_EVENT_HANDLER_HPP | |||||
#define ANTKEEPER_EVENT_HANDLER_HPP | |||||
#include "event.hpp" | |||||
#include <type_traits> | |||||
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 <typename T> | |||||
class event_handler: public event_handler_base | |||||
{ | |||||
public: | |||||
static_assert(std::is_base_of<event_base, T>::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 <typename T> | |||||
inline const std::size_t event_handler<T>::get_handled_event_type_id() const | |||||
{ | |||||
return T::event_type_id; | |||||
} | |||||
template <typename T> | |||||
void event_handler<T>::route_event(const event_base& event) | |||||
{ | |||||
handle_event(static_cast<const T&>(event)); | |||||
} | |||||
#endif // ANTKEEPER_EVENT_HANDLER_HPP | |||||
@ -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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#ifndef ANTKEEPER_EVENT_HPP | |||||
#define ANTKEEPER_EVENT_HPP | |||||
#include <atomic> | |||||
#include <cstddef> | |||||
/** | |||||
* 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<std::size_t> next_event_type_id{0}; | |||||
return next_event_type_id++; | |||||
} | |||||
/** | |||||
* Templated abstract base class for events. | |||||
* | |||||
* @tparam T The derived class. | |||||
*/ | |||||
template <typename T> | |||||
class event: public event_base | |||||
{ | |||||
public: | |||||
/// The unique event type identifier for this event type. | |||||
static const std::atomic<std::size_t> 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 <typename T> | |||||
const std::atomic<std::size_t> event<T>::event_type_id{event_base::next_event_type_id()}; | |||||
template <typename T> | |||||
inline const std::size_t event<T>::get_event_type_id() const | |||||
{ | |||||
return event_type_id; | |||||
} | |||||
#endif // ANTKEEPER_EVENT_HPP | |||||
@ -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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#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; | |||||
} | |||||
@ -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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#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<key_pressed_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<key_released_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<mouse_moved_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<mouse_button_pressed_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<mouse_button_released_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<mouse_wheel_scrolled_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<gamepad_connected_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<gamepad_disconnected_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<gamepad_button_pressed_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<gamepad_button_released_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<gamepad_axis_moved_event> | |||||
{ | |||||
public: | |||||
virtual event_base* clone() const; | |||||
input::gamepad* controller; | |||||
input::gamepad_axis axis; | |||||
float value; | |||||
}; | |||||
#endif // ANTKEEPER_INPUT_EVENTS_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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#ifndef ANTKEEPER_EVENT_PUBLISHER_HPP | |||||
#define ANTKEEPER_EVENT_PUBLISHER_HPP | |||||
#include <algorithm> | |||||
#include <execution> | |||||
#include "event/channel.hpp" | |||||
namespace event { | |||||
/** | |||||
* Publishes messages to subscribers. | |||||
* | |||||
* @tparam T Message type. | |||||
*/ | |||||
template <class T> | |||||
class publisher | |||||
{ | |||||
public: | |||||
/// Message type. | |||||
typedef T message_type; | |||||
/// Channel type. | |||||
typedef channel<message_type> channel_type; | |||||
/** | |||||
* Publishes a message. | |||||
* | |||||
* @tparam ExecutionPolicy Execution policy type. | |||||
* | |||||
* @param policy Execution policy to use. | |||||
* @param message Message to publish. | |||||
*/ | |||||
/// @{ | |||||
template <class ExecutionPolicy> | |||||
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 |
@ -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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#ifndef ANTKEEPER_EVENT_QUEUE_HPP | |||||
#define ANTKEEPER_EVENT_QUEUE_HPP | |||||
#include <any> | |||||
#include <functional> | |||||
#include <list> | |||||
#include <map> | |||||
#include <memory> | |||||
#include <utility> | |||||
#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 <class T> | |||||
[[nodiscard]] std::shared_ptr<subscription> subscribe(subscriber<T>&& subscriber) | |||||
{ | |||||
// Allocate shared pointer to std::any object containing subscriber | |||||
std::shared_ptr<std::any> shared_subscriber = std::make_shared<std::any>(std::make_any<event::subscriber<T>>(std::move(subscriber))); | |||||
// Append subscriber to subscriber list and store iterator | |||||
auto iterator = subscribers.emplace(type_id<T>, 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<subscription> | |||||
( | |||||
std::static_pointer_cast<void>(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 <class T> | |||||
void enqueue(const T& message) | |||||
{ | |||||
messages.emplace_back | |||||
( | |||||
[this, message]() | |||||
{ | |||||
this->distribute<T>(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 <class T> | |||||
void distribute(const T& message) const | |||||
{ | |||||
// For each subscriber of the given message type | |||||
const auto range = subscribers.equal_range(type_id<T>); | |||||
for (auto i = range.first; i != range.second; ++i) | |||||
{ | |||||
// Send message to subscriber | |||||
std::any_cast<subscriber<T>>(*(i->second))(message); | |||||
} | |||||
} | |||||
std::multimap<type_id_t, std::shared_ptr<std::any>> subscribers; | |||||
std::list<std::function<void()>> messages; | |||||
}; | |||||
} // namespace event | |||||
#endif // ANTKEEPER_EVENT_QUEUE_HPP |
@ -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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#ifndef ANTKEEPER_EVENT_SIGNAL_HPP | |||||
#define ANTKEEPER_EVENT_SIGNAL_HPP | |||||
#include <algorithm> | |||||
#include <execution> | |||||
#include <functional> | |||||
#include <list> | |||||
#include <memory> | |||||
#include <type_traits> | |||||
//namespace event { | |||||
template <class T> | |||||
class signal; | |||||
template <class T> | |||||
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<void(std::weak_ptr<void>)> 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<void> 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 <class T> | |||||
friend class signal; | |||||
std::weak_ptr<void> handler; | |||||
disconnector_type disconnector; | |||||
}; | |||||
/** | |||||
* Creates connections between a signal and signal handlers. | |||||
* | |||||
* @tparam T Signal response type. | |||||
* @tparam Args Signal argument types. | |||||
*/ | |||||
template <class T, class... Args> | |||||
class connector<T(Args...)> | |||||
{ | |||||
public: | |||||
/// Signal response type. | |||||
typedef T response_type; | |||||
/// Signal type. | |||||
typedef signal<T(Args...)> 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<connection> 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 T, class... Args> | |||||
class signal<T(Args...)> | |||||
{ | |||||
public: | |||||
/// Signal response type. | |||||
typedef T response_type; | |||||
/// Signal handler type. | |||||
typedef std::function<T(Args...)> handler_type; | |||||
/// Signal connector type. | |||||
typedef connector<T(Args...)> 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<connection> connect(handler_type handler) | |||||
{ | |||||
// Allocate shared pointer to handler | |||||
std::shared_ptr<handler_type> shared_handler = std::make_shared<handler_type>(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<connection> | |||||
( | |||||
std::static_pointer_cast<void>(shared_handler), | |||||
[this](std::weak_ptr<void> handler) | |||||
{ | |||||
this->connections.remove(std::static_pointer_cast<handler_type>(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 <class ExecutionPolicy> | |||||
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 <class ExecutionPolicy, class UnaryFunction> | |||||
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<T>) | |||||
{ | |||||
(*handler)(args...); | |||||
listener(); | |||||
} | |||||
else | |||||
{ | |||||
listener((*handler)(args...)); | |||||
} | |||||
} | |||||
); | |||||
} | |||||
template <class UnaryFunction> | |||||
void ping(UnaryFunction listener, Args... args) const | |||||
{ | |||||
ping(std::execution::seq, listener, args...); | |||||
} | |||||
/// @} | |||||
private: | |||||
/// List of connected signal handlers. | |||||
std::list<std::shared_ptr<handler_type>> connections; | |||||
/// Signal connector. | |||||
connector_type signal_connector; | |||||
}; | |||||
//} // namespace event | |||||
#endif // ANTKEEPER_EVENT_SIGNAL_HPP |
@ -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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#include "event/subscription.hpp" | |||||
#include <utility> | |||||
namespace event { | |||||
subscription::subscription(std::weak_ptr<void>&& 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 |
@ -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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#ifndef ANTKEEPER_EVENT_SUBSCRIPTION_HPP | |||||
#define ANTKEEPER_EVENT_SUBSCRIPTION_HPP | |||||
#include <functional> | |||||
#include <memory> | |||||
namespace event { | |||||
/** | |||||
* Subscription object which unsubscribes on destruction. | |||||
*/ | |||||
class subscription | |||||
{ | |||||
public: | |||||
/// Unsubscribe function object type. | |||||
typedef std::function<void()> unsubscribe_type; | |||||
/** | |||||
* Constructs a subscription. | |||||
* | |||||
* @param subscriber Weak pointer to the subscriber. | |||||
* @param unsubscriber Unsubscribe function object. | |||||
*/ | |||||
subscription(std::weak_ptr<void>&& 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<void> subscriber; | |||||
unsubscribe_type unsubscriber; | |||||
}; | |||||
} // namespace event | |||||
#endif // ANTKEEPER_EVENT_SUBSCRIPTION_HPP |
@ -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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#include "input/control-map.hpp" | |||||
#include <algorithm> | |||||
#include <cmath> | |||||
#include <type_traits> | |||||
#include <utility> | |||||
namespace input { | |||||
void control_map::connect(::event::queue& queue) | |||||
{ | |||||
subscriptions.emplace_back(queue.subscribe<event::gamepad_axis_moved>(std::bind_front(&control_map::handle_gamepad_axis_moved, this))); | |||||
subscriptions.emplace_back(queue.subscribe<event::gamepad_button_pressed>(std::bind_front(&control_map::handle_gamepad_button_pressed, this))); | |||||
subscriptions.emplace_back(queue.subscribe<event::gamepad_button_released>(std::bind_front(&control_map::handle_gamepad_button_released, this))); | |||||
subscriptions.emplace_back(queue.subscribe<event::key_pressed>(std::bind_front(&control_map::handle_key_pressed, this))); | |||||
subscriptions.emplace_back(queue.subscribe<event::key_released>(std::bind_front(&control_map::handle_key_released, this))); | |||||
subscriptions.emplace_back(queue.subscribe<event::mouse_button_pressed>(std::bind_front(&control_map::handle_mouse_button_pressed, this))); | |||||
subscriptions.emplace_back(queue.subscribe<event::mouse_button_released>(std::bind_front(&control_map::handle_mouse_button_released, this))); | |||||
subscriptions.emplace_back(queue.subscribe<event::mouse_moved>(std::bind_front(&control_map::handle_mouse_moved, this))); | |||||
subscriptions.emplace_back(queue.subscribe<event::mouse_scrolled>(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<const gamepad_axis_mapping&>(mapping)); | |||||
break; | |||||
case mapping_type::gamepad_button: | |||||
add_mapping(control, static_cast<const gamepad_button_mapping&>(mapping)); | |||||
break; | |||||
case mapping_type::key: | |||||
add_mapping(control, static_cast<const key_mapping&>(mapping)); | |||||
break; | |||||
case mapping_type::mouse_button: | |||||
add_mapping(control, static_cast<const mouse_button_mapping&>(mapping)); | |||||
break; | |||||
case mapping_type::mouse_motion: | |||||
add_mapping(control, static_cast<const mouse_motion_mapping&>(mapping)); | |||||
break; | |||||
case mapping_type::mouse_scroll: | |||||
add_mapping(control, static_cast<const mouse_scroll_mapping&>(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<float>(event.difference[static_cast<std::underlying_type_t<mouse_motion_axis>>(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<std::underlying_type_t<mouse_scroll_axis>>(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 |
@ -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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#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 <memory> | |||||
#include <tuple> | |||||
#include <unordered_map> | |||||
#include <vector> | |||||
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<std::shared_ptr<::event::subscription>> subscriptions; | |||||
std::vector<std::tuple<control*, gamepad_axis_mapping>> gamepad_axis_mappings; | |||||
std::vector<std::tuple<control*, gamepad_button_mapping>> gamepad_button_mappings; | |||||
std::vector<std::tuple<control*, key_mapping>> key_mappings; | |||||
std::vector<std::tuple<control*, mouse_button_mapping>> mouse_button_mappings; | |||||
std::vector<std::tuple<control*, mouse_motion_mapping>> mouse_motion_mappings; | |||||
std::vector<std::tuple<control*, mouse_scroll_mapping>> mouse_scroll_mappings; | |||||
}; | |||||
} // namespace input | |||||
#endif // ANTKEEER_INPUT_CONTROL_MAP_HPP |
@ -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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#ifndef ANTKEEPER_INPUT_CONTROL_SET_HPP | |||||
#define ANTKEEPER_INPUT_CONTROL_SET_HPP | |||||
#include <list> | |||||
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<control*>* get_controls() const; | |||||
private: | |||||
std::list<control*> controls; | |||||
}; | |||||
inline const std::list<control*>* control_set::get_controls() const | |||||
{ | |||||
return &controls; | |||||
} | |||||
} // namespace input | |||||
#endif // ANTKEEPER_INPUT_CONTROL_SET_HPP | |||||
@ -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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#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<gamepad&>(device)); | |||||
break; | |||||
case device_type::keyboard: | |||||
register_keyboard(static_cast<keyboard&>(device)); | |||||
break; | |||||
case device_type::mouse: | |||||
register_mouse(static_cast<mouse&>(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<gamepad&>(device)); | |||||
break; | |||||
case device_type::keyboard: | |||||
unregister_keyboard(static_cast<keyboard&>(device)); | |||||
break; | |||||
case device_type::mouse: | |||||
unregister_mouse(static_cast<mouse&>(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 |
@ -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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#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 <map> | |||||
#include <memory> | |||||
#include <unordered_set> | |||||
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<gamepad*>& get_gamepads() noexcept | |||||
{ | |||||
return gamepads; | |||||
} | |||||
/// Returns the set of registered keyboards. | |||||
[[nodiscard]] inline const std::unordered_set<keyboard*>& get_keyboards() noexcept | |||||
{ | |||||
return keyboards; | |||||
} | |||||
/// Returns the set of registered mice. | |||||
[[nodiscard]] inline const std::unordered_set<mouse*>& 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<device*, std::shared_ptr<::event::subscription>> subscriptions; | |||||
std::unordered_set<gamepad*> gamepads; | |||||
std::unordered_set<keyboard*> keyboards; | |||||
std::unordered_set<mouse*> mice; | |||||
}; | |||||
} // namespace input | |||||
#endif // ANTKEEPER_INPUT_DEVICE_MANAGER_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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#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 |
@ -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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#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<const input::key_mapping&>(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<const input::mouse_motion_mapping&>(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<const input::mouse_wheel_mapping&>(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<const input::mouse_button_mapping&>(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<const input::gamepad_axis_mapping&>(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<const input::gamepad_button_mapping&>(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<key_mapping*>(mapping)); | |||||
break; | |||||
case mapping_type::mouse_motion: | |||||
mouse_motion_mappings.remove(static_cast<mouse_motion_mapping*>(mapping)); | |||||
break; | |||||
case mapping_type::mouse_wheel: | |||||
mouse_wheel_mappings.remove(static_cast<mouse_wheel_mapping*>(mapping)); | |||||
break; | |||||
case mapping_type::mouse_button: | |||||
mouse_button_mappings.remove(static_cast<mouse_button_mapping*>(mapping)); | |||||
break; | |||||
case mapping_type::gamepad_axis: | |||||
gamepad_axis_mappings.remove(static_cast<gamepad_axis_mapping*>(mapping)); | |||||
break; | |||||
case mapping_type::gamepad_button: | |||||
gamepad_button_mappings.remove(static_cast<gamepad_button_mapping*>(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<mapping*> 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<key_mapping*>(mapping)); | |||||
break; | |||||
case mapping_type::mouse_motion: | |||||
mouse_motion_mappings.remove(static_cast<mouse_motion_mapping*>(mapping)); | |||||
break; | |||||
case mapping_type::mouse_wheel: | |||||
mouse_wheel_mappings.remove(static_cast<mouse_wheel_mapping*>(mapping)); | |||||
break; | |||||
case mapping_type::mouse_button: | |||||
mouse_button_mappings.remove(static_cast<mouse_button_mapping*>(mapping)); | |||||
break; | |||||
case mapping_type::gamepad_axis: | |||||
gamepad_axis_mappings.remove(static_cast<gamepad_axis_mapping*>(mapping)); | |||||
break; | |||||
case mapping_type::gamepad_button: | |||||
gamepad_button_mappings.remove(static_cast<gamepad_button_mapping*>(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<key_pressed_event>(this); | |||||
this->event_dispatcher->unsubscribe<key_released_event>(this); | |||||
this->event_dispatcher->unsubscribe<mouse_moved_event>(this); | |||||
this->event_dispatcher->unsubscribe<mouse_wheel_scrolled_event>(this); | |||||
this->event_dispatcher->unsubscribe<mouse_button_pressed_event>(this); | |||||
this->event_dispatcher->unsubscribe<mouse_button_released_event>(this); | |||||
this->event_dispatcher->unsubscribe<gamepad_axis_moved_event>(this); | |||||
this->event_dispatcher->unsubscribe<gamepad_button_pressed_event>(this); | |||||
this->event_dispatcher->unsubscribe<gamepad_button_released_event>(this); | |||||
} | |||||
this->event_dispatcher = event_dispatcher; | |||||
if (event_dispatcher) | |||||
{ | |||||
event_dispatcher->subscribe<key_pressed_event>(this); | |||||
event_dispatcher->subscribe<key_released_event>(this); | |||||
event_dispatcher->subscribe<mouse_moved_event>(this); | |||||
event_dispatcher->subscribe<mouse_wheel_scrolled_event>(this); | |||||
event_dispatcher->subscribe<mouse_button_pressed_event>(this); | |||||
event_dispatcher->subscribe<mouse_button_released_event>(this); | |||||
event_dispatcher->subscribe<gamepad_axis_moved_event>(this); | |||||
event_dispatcher->subscribe<gamepad_button_pressed_event>(this); | |||||
event_dispatcher->subscribe<gamepad_button_released_event>(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<mapping*>* 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 |
@ -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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#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 <list> | |||||
#include <map> | |||||
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<key_pressed_event>, | |||||
public event_handler<key_released_event>, | |||||
public event_handler<mouse_moved_event>, | |||||
public event_handler<mouse_wheel_scrolled_event>, | |||||
public event_handler<mouse_button_pressed_event>, | |||||
public event_handler<mouse_button_released_event>, | |||||
public event_handler<gamepad_axis_moved_event>, | |||||
public event_handler<gamepad_button_pressed_event>, | |||||
public event_handler<gamepad_button_released_event> | |||||
{ | |||||
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<mapping*>* 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<control*, std::list<mapping*>> controls; | |||||
std::list<key_mapping*> key_mappings; | |||||
std::list<mouse_motion_mapping*> mouse_motion_mappings; | |||||
std::list<mouse_wheel_mapping*> mouse_wheel_mappings; | |||||
std::list<mouse_button_mapping*> mouse_button_mappings; | |||||
std::list<gamepad_axis_mapping*> gamepad_axis_mappings; | |||||
std::list<gamepad_button_mapping*> gamepad_button_mappings; | |||||
}; | |||||
} // namespace input | |||||
#endif // ANTKEEER_INPUT_EVENT_ROUTER_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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#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 <cstdint> | |||||
#include <memory> | |||||
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> 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<std::int32_t, 2> position; | |||||
/// Relative movement of the mouse, in pixels. | |||||
math::vector<std::int32_t, 2> 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<std::int32_t, 2> 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<std::int32_t, 2> 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<std::int32_t, 2> position; | |||||
/// Scroll velocity. | |||||
math::vector<float, 2> 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<std::int32_t, 2> 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 |
@ -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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#ifndef ANTKEEPER_INPUT_GAMEPAD_AXIS_HPP | |||||
#define ANTKEEPER_INPUT_GAMEPAD_AXIS_HPP | |||||
#include <cstdint> | |||||
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 |
@ -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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#ifndef ANTKEEPER_INPUT_GAMEPAD_BUTTON_HPP | |||||
#define ANTKEEPER_INPUT_GAMEPAD_BUTTON_HPP | |||||
#include <cstdint> | |||||
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 |
@ -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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#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 |
@ -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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#include "listener.hpp" | |||||
#include "event/event-dispatcher.hpp" | |||||
#include <iostream> | |||||
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<key_pressed_event>(this); | |||||
this->event_dispatcher->unsubscribe<mouse_moved_event>(this); | |||||
this->event_dispatcher->unsubscribe<mouse_wheel_scrolled_event>(this); | |||||
this->event_dispatcher->unsubscribe<mouse_button_pressed_event>(this); | |||||
this->event_dispatcher->unsubscribe<gamepad_axis_moved_event>(this); | |||||
this->event_dispatcher->unsubscribe<gamepad_button_pressed_event>(this); | |||||
} | |||||
if (event_dispatcher) | |||||
{ | |||||
event_dispatcher->subscribe<key_pressed_event>(this); | |||||
event_dispatcher->subscribe<mouse_moved_event>(this); | |||||
event_dispatcher->subscribe<mouse_wheel_scrolled_event>(this); | |||||
event_dispatcher->subscribe<mouse_button_pressed_event>(this); | |||||
event_dispatcher->subscribe<gamepad_axis_moved_event>(this); | |||||
event_dispatcher->subscribe<gamepad_button_pressed_event>(this); | |||||
} | |||||
this->event_dispatcher = event_dispatcher; | |||||
} | |||||
} | |||||
void listener::set_callback(std::function<void(const event_base&)> 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 |
@ -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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#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 <functional> | |||||
namespace input { | |||||
/** | |||||
* Listens for all types of input events. | |||||
*/ | |||||
class listener: | |||||
public event_handler<key_pressed_event>, | |||||
public event_handler<mouse_moved_event>, | |||||
public event_handler<mouse_wheel_scrolled_event>, | |||||
public event_handler<mouse_button_pressed_event>, | |||||
public event_handler<gamepad_axis_moved_event>, | |||||
public event_handler<gamepad_button_pressed_event> | |||||
{ | |||||
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<void(const event_base&)> 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<void(const event_base&)> callback; | |||||
bool enabled; | |||||
}; | |||||
inline bool listener::is_enabled() const | |||||
{ | |||||
return enabled; | |||||
} | |||||
} // namespace input | |||||
#endif // ANTKEEPER_INPUT_LISTENER_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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#ifndef ANTKEEPER_INPUT_MODIFIER_KEY_HPP | |||||
#define ANTKEEPER_INPUT_MODIFIER_KEY_HPP | |||||
#include <cstdint> | |||||
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 |
@ -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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#ifndef ANTKEEPER_INPUT_MOUSE_BUTTON_HPP | |||||
#define ANTKEEPER_INPUT_MOUSE_BUTTON_HPP | |||||
#include <cstdint> | |||||
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 |