From b74611d2b20fc071b8a699f2ce25e61f60118d6e Mon Sep 17 00:00:00 2001 From: HampusM Date: Sun, 22 May 2022 23:13:29 +0200 Subject: refactor: improve input handling & remove commands --- src/engine/engine.cpp | 24 ++++----------- src/engine/engine.hpp | 11 +++---- src/engine/graphics/scene.cpp | 34 ++++++++++++++++++---- src/engine/graphics/scene.hpp | 6 ++-- src/engine/user/input.cpp | 68 +++++++++++++++---------------------------- src/engine/user/input.hpp | 37 +++++++---------------- 6 files changed, 76 insertions(+), 104 deletions(-) (limited to 'src/engine') diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 774220a..c7605b5 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -9,11 +9,11 @@ CLIGameEngine::CLIGameEngine( IGameFactory game_factory, ISceneFactory scene_factory, - std::shared_ptr input_handler, + std::shared_ptr user_input_observer, std::shared_ptr cursor_controller) noexcept : _game_factory(std::move(game_factory)), _scene_factory(std::move(scene_factory)), - _input_handler(std::move(input_handler)), + _user_input_observer(std::move(user_input_observer)), _cursor_controller(std::move(cursor_controller)) { } @@ -23,9 +23,8 @@ void CLIGameEngine::start() noexcept std::shared_ptr scene = _scene_factory(_cursor_controller); scene->enter(); - _input_handler->enter_raw_mode(); - auto game = _game_factory(scene, _cursor_controller); + auto game = _game_factory(scene, _cursor_controller, _user_input_observer); game->on_start(); @@ -33,17 +32,14 @@ void CLIGameEngine::start() noexcept [this, scene, &game]() { scene->leave(); - _input_handler->leave_raw_mode(); game->on_exit(); })); - _configure_input(game->get_input_config()); - auto listen_input_thread = std::thread(normalize_lambda( [this]() { - _input_handler->listen(); + _user_input_observer->listen(); })); auto last_update_time = std::chrono::system_clock::now(); @@ -68,17 +64,7 @@ void CLIGameEngine::start() noexcept game->on_update(); last_update_time = std::chrono::system_clock::now(); - } -} - -void CLIGameEngine::_configure_input( - const std::unordered_map> &input_config) noexcept -{ - for (const auto &config_pair : input_config) - { - auto key = config_pair.first; - auto command = config_pair.second; - _input_handler->subscribe(key, command); + _user_input_observer->clear_currently_pressed(); } } diff --git a/src/engine/engine.hpp b/src/engine/engine.hpp index 1e8a217..b34ea2b 100644 --- a/src/engine/engine.hpp +++ b/src/engine/engine.hpp @@ -12,7 +12,7 @@ #include #include -constexpr auto MIN_TIME_SINCE_LAST_UPDATE_MILLIS = 100; +constexpr auto MIN_TIME_SINCE_LAST_UPDATE_MILLIS = 40; class CLIGameEngine : public ICLIGameEngine, public yacppdic::AutoWirable< @@ -20,14 +20,14 @@ class CLIGameEngine : public ICLIGameEngine, CLIGameEngine, IGameFactory, ISceneFactory, - IInputHandler, + IUserInputObserver, ICursorController> { public: CLIGameEngine( IGameFactory game_factory, ISceneFactory scene_factory, - std::shared_ptr input_handler, + std::shared_ptr user_input_observer, std::shared_ptr cursor_controller) noexcept; void start() noexcept override; @@ -36,9 +36,6 @@ private: IGameFactory _game_factory; ISceneFactory _scene_factory; - std::shared_ptr _input_handler; + std::shared_ptr _user_input_observer; std::shared_ptr _cursor_controller; - - void _configure_input( - const std::unordered_map> &input_config) noexcept; }; diff --git a/src/engine/graphics/scene.cpp b/src/engine/graphics/scene.cpp index 52613d8..b0d77b8 100644 --- a/src/engine/graphics/scene.cpp +++ b/src/engine/graphics/scene.cpp @@ -13,36 +13,60 @@ Scene::Scene( IMatrixFactory matrix_factory, std::shared_ptr cursor_controller) noexcept - : _is_shown(false), - _matrix(matrix_factory(size() - Bounds({.width = 0U, .height = 1U}))), - _cursor_controller(std::move(cursor_controller)) + : _matrix(matrix_factory(size() - Bounds({.width = 0U, .height = 1U}))), + _cursor_controller(std::move(cursor_controller)), + _is_shown(false) { _matrix->fill(" "); } void Scene::enter() noexcept { - if (_is_shown) + if (_is_shown || _original_termios != nullptr) { return; } + // Enable alternative buffer fmt::print(ENABLE_ALT_BUFFER, fmt::arg("esc", ESC)); std::cout.flush(); + // Create a backup of the current terminal state + _original_termios = std::make_shared(); + tcgetattr(STDIN_FILENO, _original_termios.get()); + + auto new_termios = termios(*_original_termios); + + // Set the local mode flags of the new termios structure + // + // The following flags are disabled: + // ECHO - Echoing input characters + // ICANON - Canonical mode (line by line input) + // ISIG - Generate the corresponding signals for the characters + // INTR, QUIT, SUSP and DSUSP + new_termios.c_lflag &= static_cast(~(ECHO | ICANON | ISIG)); + + // Set a new terminal state + tcsetattr(STDIN_FILENO, TCSAFLUSH, &new_termios); + _is_shown = true; } void Scene::leave() noexcept { - if (!_is_shown) + if (!_is_shown || _original_termios == nullptr) { return; } + // Disable alternative buffer fmt::print(DISABLE_ALT_BUFFER, fmt::arg("esc", ESC)); std::cout.flush(); + // Restore the original terminal state + tcsetattr(STDIN_FILENO, TCSAFLUSH, _original_termios.get()); + + _original_termios = nullptr; _is_shown = false; } diff --git a/src/engine/graphics/scene.hpp b/src/engine/graphics/scene.hpp index 268f8dc..e57e1f8 100644 --- a/src/engine/graphics/scene.hpp +++ b/src/engine/graphics/scene.hpp @@ -8,6 +8,7 @@ #include #include +#include constexpr fmt::string_view ENABLE_ALT_BUFFER = "{esc}[?1049h"; constexpr fmt::string_view DISABLE_ALT_BUFFER = "{esc}[?1049l"; @@ -29,8 +30,9 @@ public: -> const std::shared_ptr> & override; private: - bool _is_shown; - std::shared_ptr> _matrix; std::shared_ptr _cursor_controller; + + bool _is_shown; + std::shared_ptr _original_termios = nullptr; }; diff --git a/src/engine/user/input.cpp b/src/engine/user/input.cpp index 7e9fe22..ac6d660 100644 --- a/src/engine/user/input.cpp +++ b/src/engine/user/input.cpp @@ -3,66 +3,46 @@ #include #include -void InputHandler::listen() const noexcept -{ - char character = 0; +#include - while (!std::cin.read(&character, 1).fail()) +void UserInputObserver::listen() noexcept +{ + while (true) { - notify_subscribers(character, nullptr); - } -} + char character = 0; -void InputHandler::subscribe( - const Event &event, - const std::shared_ptr> &subscriber) noexcept -{ - auto event_index = _event_as_index(event); + if (std::cin.read(&character, 1).fail()) + { + continue; + } - _subscribers.at(event_index).push_back(subscriber); -} + _currently_pressed_mutex.lock(); -void InputHandler::notify_subscribers(const Event &event, const Context &context) - const noexcept -{ - auto event_index = _event_as_index(event); + if (character != _currently_pressed) + { + _currently_pressed = character; + } - for (const auto &subscriber : _subscribers.at(event_index)) - { - subscriber->update(context); + _currently_pressed_mutex.unlock(); } } -void InputHandler::enter_raw_mode() noexcept +bool UserInputObserver::is_key_pressed(Key key) noexcept { - if (_original_termios == nullptr) - { - _original_termios = std::make_shared(); - } + _currently_pressed_mutex.lock(); - tcgetattr(STDIN_FILENO, _original_termios.get()); + const auto is_key_pressed = key == _currently_pressed; - auto raw_termios = termios(*_original_termios); + _currently_pressed_mutex.unlock(); - raw_termios.c_lflag &= static_cast(~(ECHO | ICANON | ISIG)); - - tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw_termios); + return is_key_pressed; } -void InputHandler::leave_raw_mode() noexcept +void UserInputObserver::clear_currently_pressed() noexcept { - if (_original_termios == nullptr) - { - return; - } - - tcsetattr(STDIN_FILENO, TCSAFLUSH, _original_termios.get()); + _currently_pressed_mutex.lock(); - _original_termios = nullptr; -} + _currently_pressed = 0; -auto InputHandler::_event_as_index(const Event &event) noexcept - -> InputHandler::SubscribersSizeType -{ - return static_cast(static_cast(event)); + _currently_pressed_mutex.unlock(); } diff --git a/src/engine/user/input.hpp b/src/engine/user/input.hpp index f17472b..2fb97ac 100644 --- a/src/engine/user/input.hpp +++ b/src/engine/user/input.hpp @@ -5,40 +5,23 @@ #include -#include -#include #include -#include -#include +#include -class InputHandler : public IInputHandler, - public yacppdic::AutoWirable +class UserInputObserver + : public IUserInputObserver, + public yacppdic::AutoWirable { public: - InputHandler() noexcept = default; + UserInputObserver() noexcept = default; - void listen() const noexcept override; + void listen() noexcept override; - void subscribe( - const Event &event, - const std::shared_ptr> &subscriber) noexcept override; + bool is_key_pressed(Key key) noexcept override; - void notify_subscribers(const Event &event, const Context &context) - const noexcept override; - - void enter_raw_mode() noexcept override; - - void leave_raw_mode() noexcept override; + void clear_currently_pressed() noexcept override; private: - std::array< - std::vector>>, - static_cast(CHAR_MAX * 2U)> - _subscribers; - - std::shared_ptr _original_termios = nullptr; - - using SubscribersSizeType = decltype(_subscribers)::size_type; - - static auto _event_as_index(const Event &event) noexcept -> SubscribersSizeType; + Key _currently_pressed; + std::mutex _currently_pressed_mutex; }; -- cgit v1.2.3-18-g5258