From db2cf722c5e5b5399c8163a1d77cccf963c6fa78 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 22 Sep 2026 15:46:22 +0200 Subject: lib/routers: handle navigation keys in echo router --- ttwhy/routers/echo.cppm | 140 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 122 insertions(+), 18 deletions(-) (limited to 'ttwhy/routers/echo.cppm') 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 +#include + +#include #include #include @@ -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 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 { co_await std::visit( @@ -53,7 +71,13 @@ namespace ttwhy::routers private: auto process_character(char character) -> asio::awaitable { - 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 @@ -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 { - 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(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(event.key), static_cast(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 -- cgit v1.2.3