aboutsummaryrefslogtreecommitdiff
path: root/ttwhy/terminal
diff options
context:
space:
mode:
Diffstat (limited to 'ttwhy/terminal')
-rw-r--r--ttwhy/terminal/echo.cppm122
1 files changed, 122 insertions, 0 deletions
diff --git a/ttwhy/terminal/echo.cppm b/ttwhy/terminal/echo.cppm
index 6f1ac2e..19d15fc 100644
--- a/ttwhy/terminal/echo.cppm
+++ b/ttwhy/terminal/echo.cppm
@@ -27,6 +27,106 @@ namespace ttwhy::terminal
constexpr auto vte_cursor_left_sequence_format = "\x1b[{}D"sv;
constexpr auto vte_cursor_right_sequence_format = "\x1b[{}C"sv;
+ namespace detail
+ {
+ //! Maps a function key to its CSI parameter code (F5 through F20).
+ auto function_key_parameter(function_key key) -> unsigned int
+ {
+ switch (key)
+ {
+ case function_key::f5:
+ return 15;
+ case function_key::f6:
+ return 17;
+ case function_key::f7:
+ return 18;
+ case function_key::f8:
+ return 19;
+ case function_key::f9:
+ return 20;
+ case function_key::f10:
+ return 21;
+ case function_key::f11:
+ return 23;
+ case function_key::f12:
+ return 24;
+ case function_key::f13:
+ return 25;
+ case function_key::f14:
+ return 26;
+ case function_key::f15:
+ return 28;
+ case function_key::f16:
+ return 29;
+ case function_key::f17:
+ return 31;
+ case function_key::f18:
+ return 32;
+ case function_key::f19:
+ return 33;
+ case function_key::f20:
+ return 34;
+ default:
+ return 0;
+ }
+ }
+
+ //! Appends an xterm-style modifier parameter to a CSI sequence.
+ auto apply_modifier(std::string sequence, modifier_set modifiers) -> std::string
+ {
+ auto const mask = static_cast<std::uint8_t>(modifiers);
+ auto const modifier_param = [&mask]() -> unsigned int {
+ if ((mask & static_cast<std::uint8_t>(modifier::shift)) != 0)
+ {
+ return 2;
+ }
+ if ((mask & static_cast<std::uint8_t>(modifier::alt)) != 0)
+ {
+ return 3;
+ }
+ if ((mask & static_cast<std::uint8_t>(modifier::ctrl)) != 0)
+ {
+ return 4;
+ }
+ if ((mask & static_cast<std::uint8_t>(modifier::meta)) != 0)
+ {
+ return 5;
+ }
+ return 1;
+ }();
+
+ if (modifier_param == 1)
+ {
+ return sequence;
+ }
+
+ auto const terminator = sequence.find_last_of('~');
+ if (terminator == std::string::npos)
+ {
+ return sequence;
+ }
+ return sequence.substr(0, terminator) + std::to_string(modifier_param) + sequence.substr(terminator);
+ }
+
+ //! Builds the terminal escape sequence that represents a function key press.
+ auto function_key_sequence(function_key key, modifier_set modifiers) -> std::string
+ {
+ switch (key)
+ {
+ case function_key::f1:
+ return apply_modifier(std::string{"\x1bOP"}, modifiers);
+ case function_key::f2:
+ return apply_modifier(std::string{"\x1bOQ"}, modifiers);
+ case function_key::f3:
+ return apply_modifier(std::string{"\x1bOR"}, modifiers);
+ case function_key::f4:
+ return apply_modifier(std::string{"\x1bOS"}, modifiers);
+ default:
+ return apply_modifier(std::format("\x1b[{}~", function_key_parameter(key)), modifiers);
+ }
+ }
+ } // namespace detail
+
//! An asynchronous router that echoes characters and handles terminal navigation.
//!
//! @tparam StreamType The output stream descriptor type.
@@ -78,11 +178,33 @@ namespace ttwhy::terminal
{
co_await process_navigation_key(event);
}
+ else if constexpr (std::same_as<function_key_event, event_type>)
+ {
+ co_await process_function_key(event);
+ }
+ else if constexpr (std::same_as<ctrl_chord_event, event_type>)
+ {
+ co_await process_ctrl_chord(event);
+ }
},
event);
}
private:
+ auto process_function_key(function_key_event event) -> asio::awaitable<void>
+ {
+ auto const sequence = detail::function_key_sequence(event.key, event.modifiers);
+ co_await asio::async_write(m_output_stream, asio::buffer(sequence.data(), sequence.size()),
+ asio::as_tuple(asio::use_awaitable));
+ }
+
+ auto process_ctrl_chord(ctrl_chord_event event) -> asio::awaitable<void>
+ {
+ auto const control_character = static_cast<char>(static_cast<unsigned char>(event.key) - 0x40u);
+ co_await asio::async_write(m_output_stream, asio::buffer(&control_character, 1),
+ asio::as_tuple(asio::use_awaitable));
+ }
+
auto process_character(char character) -> asio::awaitable<void>
{
auto [error, bytes_written] =