aboutsummaryrefslogtreecommitdiff
path: root/ttwhy/routers/echo.cppm
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@gmail.com>2026-09-22 15:46:22 +0200
committerFelix Morgner <felix.morgner@gmail.com>2026-09-22 15:46:22 +0200
commitdb2cf722c5e5b5399c8163a1d77cccf963c6fa78 (patch)
tree2f62337591b5402fd0a932a4b137db980b7e8d89 /ttwhy/routers/echo.cppm
parent10e049303048028577215157dd1fc87543ca2871 (diff)
downloadttwhy-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.cppm140
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