diff options
| author | Felix Morgner <felix.morgner@gmail.com> | 2026-09-22 15:46:22 +0200 |
|---|---|---|
| committer | Felix Morgner <felix.morgner@gmail.com> | 2026-09-22 15:46:22 +0200 |
| commit | db2cf722c5e5b5399c8163a1d77cccf963c6fa78 (patch) | |
| tree | 2f62337591b5402fd0a932a4b137db980b7e8d89 /ttwhy/routers/echo.cppm | |
| parent | 10e049303048028577215157dd1fc87543ca2871 (diff) | |
| download | ttwhy-db2cf722c5e5b5399c8163a1d77cccf963c6fa78.tar.xz ttwhy-db2cf722c5e5b5399c8163a1d77cccf963c6fa78.zip | |
lib/routers: handle navigation keys in echo router
Diffstat (limited to 'ttwhy/routers/echo.cppm')
| -rw-r--r-- | ttwhy/routers/echo.cppm | 140 |
1 files changed, 122 insertions, 18 deletions
diff --git a/ttwhy/routers/echo.cppm b/ttwhy/routers/echo.cppm index 7f2550e..6fe60cb 100644 --- a/ttwhy/routers/echo.cppm +++ b/ttwhy/routers/echo.cppm @@ -2,6 +2,9 @@ module; #include <asio.hpp> +#include <algorithm> + +#include <cstddef> #include <format> #include <string_view> @@ -17,7 +20,12 @@ namespace ttwhy::routers constexpr auto vte_backspace_sequence = "\b \b"sv; constexpr auto vte_newline_sequence = "\r\n"sv; - constexpr auto vte_horizontal_tab_sequqnce = "\t"sv; + constexpr auto vte_horizontal_tab_sequence = "\t"sv; + constexpr auto vte_cursor_left_sequence = "\x1b[D"sv; + constexpr auto vte_cursor_right_sequence = "\x1b[C"sv; + constexpr auto vte_delete_character_sequence = "\x1b[P"sv; + constexpr auto vte_cursor_left_sequence_format = "\x1b[{}D"sv; + constexpr auto vte_cursor_right_sequence_format = "\x1b[{}C"sv; export template<typename StreamType> struct echo @@ -28,6 +36,16 @@ namespace ttwhy::routers : m_output_stream{stream} {} + [[nodiscard]] auto cursor_position() const noexcept -> std::size_t + { + return m_cursor_position; + } + + [[nodiscard]] auto max_position() const noexcept -> std::size_t + { + return m_max_position; + } + auto process(scanners::input_event event) -> asio::awaitable<void> { co_await std::visit( @@ -53,7 +71,13 @@ namespace ttwhy::routers private: auto process_character(char character) -> asio::awaitable<void> { - co_await asio::async_write(m_output_stream, asio::buffer(&character, 1), asio::as_tuple(asio::use_awaitable)); + auto [error, bytes_written] = + co_await asio::async_write(m_output_stream, asio::buffer(&character, 1), asio::as_tuple(asio::use_awaitable)); + if (!error) + { + m_cursor_position += bytes_written; + m_max_position = std::max(m_max_position, m_cursor_position); + } } auto process_control_key(scanners::control_key key) -> asio::awaitable<void> @@ -61,17 +85,43 @@ namespace ttwhy::routers switch (key) { case scanners::control_key::backspace: - co_await asio::async_write(m_output_stream, asio::buffer(vte_backspace_sequence), - asio::as_tuple(asio::use_awaitable)); + if (m_cursor_position > 0) + { + auto [error, bytes_written] = co_await asio::async_write( + m_output_stream, asio::buffer(vte_backspace_sequence), asio::as_tuple(asio::use_awaitable)); + if (!error) + { + if (m_cursor_position == m_max_position) + { + --m_max_position; + } + --m_cursor_position; + } + } break; case scanners::control_key::enter: - co_await asio::async_write(m_output_stream, asio::buffer(vte_newline_sequence), - asio::as_tuple(asio::use_awaitable)); + { + auto [error, bytes_written] = co_await asio::async_write(m_output_stream, asio::buffer(vte_newline_sequence), + asio::as_tuple(asio::use_awaitable)); + if (!error) + { + m_cursor_position = 0; + m_max_position = 0; + } break; + } case scanners::control_key::tab: - co_await asio::async_write(m_output_stream, asio::buffer(vte_horizontal_tab_sequqnce), - asio::as_tuple(asio::use_awaitable)); + { + auto [error, bytes_written] = co_await asio::async_write( + m_output_stream, asio::buffer(vte_horizontal_tab_sequence), asio::as_tuple(asio::use_awaitable)); + if (!error) + { + auto const tab_stop = 8uz - (m_cursor_position % 8uz); + m_cursor_position += tab_stop; + m_max_position = std::max(m_max_position, m_cursor_position); + } break; + } default: co_await asio::async_write(m_output_stream, asio::buffer(std::format("{{CTRL:{}}}", std::to_underlying(key))), asio::as_tuple(asio::use_awaitable)); @@ -81,25 +131,79 @@ namespace ttwhy::routers auto process_navigation_key(scanners::navigation_event event) -> asio::awaitable<void> { - auto modifiers = std::to_underlying(event.modifiers); - switch (event.key) { + case scanners::navigation_key::left: + if (m_cursor_position > 0) + { + auto [error, bytes_written] = co_await asio::async_write( + m_output_stream, asio::buffer(vte_cursor_left_sequence), asio::as_tuple(asio::use_awaitable)); + if (!error) + { + --m_cursor_position; + } + } + break; + + case scanners::navigation_key::right: + if (m_cursor_position < m_max_position) + { + auto [error, bytes_written] = co_await asio::async_write( + m_output_stream, asio::buffer(vte_cursor_right_sequence), asio::as_tuple(asio::use_awaitable)); + if (!error) + { + ++m_cursor_position; + } + } + break; + + case scanners::navigation_key::home: + if (m_cursor_position > 0) + { + auto sequence = std::format(vte_cursor_left_sequence_format, m_cursor_position); + auto [error, bytes_written] = co_await asio::async_write(m_output_stream, asio::buffer(sequence), + asio::as_tuple(asio::use_awaitable)); + if (!error) + { + m_cursor_position = 0; + } + } + break; + + case scanners::navigation_key::end: + if (m_cursor_position < m_max_position) + { + auto const distance = m_max_position - m_cursor_position; + auto sequence = std::format(vte_cursor_right_sequence_format, distance); + auto [error, bytes_written] = co_await asio::async_write(m_output_stream, asio::buffer(sequence), + asio::as_tuple(asio::use_awaitable)); + if (!error) + { + m_cursor_position = m_max_position; + } + } + break; + case scanners::navigation_key::delete_key: - { - auto message = std::format("{{DEL, MODS:{}}}", static_cast<int>(modifiers)); - co_await asio::async_write(m_output_stream, asio::buffer(message), asio::as_tuple(asio::use_awaitable)); + if (m_cursor_position < m_max_position) + { + auto [error, bytes_written] = co_await asio::async_write( + m_output_stream, asio::buffer(vte_delete_character_sequence), asio::as_tuple(asio::use_awaitable)); + if (!error) + { + --m_max_position; + } + } break; - } + default: - { - auto message = std::format("{{NAV:{}, MODS:{}}}", static_cast<int>(event.key), static_cast<int>(modifiers)); - co_await asio::async_write(m_output_stream, asio::buffer(message), asio::as_tuple(asio::use_awaitable)); - } + break; } } StreamType & m_output_stream; + std::size_t m_cursor_position{}; + std::size_t m_max_position{}; }; } // namespace ttwhy::routers |
