module; #include #include #include export module ttwhy.terminal:policies; import :events; namespace ttwhy::terminal { namespace detail { constexpr auto parse_primary_parameter(std::string_view buffer) -> std::optional { auto delimiter = buffer.find(';'); auto primary_slice = buffer.substr(0, delimiter); if (primary_slice.empty()) { return std::nullopt; } auto value = 0u; for (auto character : primary_slice) { if (character < '0' || character > '9') { return std::nullopt; } value = value * 10 + static_cast(character - '0'); } return value; } constexpr auto map_vt220_navigation(unsigned int code) -> std::optional { switch (code) { case 1: case 7: return navigation_key::home; case 2: return navigation_key::insert_key; case 3: return navigation_key::delete_key; case 4: case 8: return navigation_key::end; case 5: return navigation_key::page_up; case 6: return navigation_key::page_down; default: return std::nullopt; } } constexpr auto map_vt220_function(unsigned int code) -> std::optional { switch (code) { case 11: return function_key::f1; case 12: return function_key::f2; case 13: return function_key::f3; case 14: return function_key::f4; case 15: return function_key::f5; case 17: return function_key::f6; case 18: return function_key::f7; case 19: return function_key::f8; case 20: return function_key::f9; case 21: return function_key::f10; case 23: return function_key::f11; case 24: return function_key::f12; case 25: return function_key::f13; case 26: return function_key::f14; case 28: return function_key::f15; case 29: return function_key::f16; case 31: return function_key::f17; case 32: return function_key::f18; case 33: return function_key::f19; case 34: return function_key::f20; default: return std::nullopt; } } constexpr auto map_vt100_cursor(char terminator) -> std::optional { switch (terminator) { case 'A': return navigation_key::up; case 'B': return navigation_key::down; case 'C': return navigation_key::right; case 'D': return navigation_key::left; case 'H': return navigation_key::home; case 'F': return navigation_key::end; default: return std::nullopt; } } constexpr auto map_ss3_function(char terminator) -> std::optional { switch (terminator) { case 'P': return function_key::f1; case 'Q': return function_key::f2; case 'R': return function_key::f3; case 'S': return function_key::f4; default: return std::nullopt; } } } // namespace detail //! Policy for decoding standard ANSI/VT escape sequences. export struct ansi_policy { //! Resolve a VT220 keypad sequence and emit the corresponding navigation or function event. //! //! @param buffer The CSI parameter buffer. //! @param sink The event sink to receive the resolved event. constexpr auto static resolve_vt220_keypad(std::string & buffer, event_sink auto & sink) -> void { if (buffer.empty()) { return; } if (auto code = detail::parse_primary_parameter(buffer)) { if (auto key = detail::map_vt220_navigation(*code)) { sink(navigation_event{*key}); } else if (auto key = detail::map_vt220_function(*code)) { sink(function_key_event{*key}); } } buffer.clear(); } //! Resolve a VT100 cursor sequence and emit the corresponding navigation event. //! //! @param terminator The CSI terminator character indicating direction or key. //! @param sink The event sink to receive the resolved navigation event. constexpr auto static resolve_vt100_cursor(char terminator, std::string &, event_sink auto & sink) -> void { if (auto key = detail::map_vt100_cursor(terminator)) { sink(navigation_event{*key}); } } //! Resolve an SS3 single-shift sequence and emit the corresponding navigation or function event. //! //! @param terminator The SS3 terminator character indicating direction or key. //! @param sink The event sink to receive the resolved event. constexpr auto static resolve_ss3(char terminator, event_sink auto & sink) -> void { if (auto key = detail::map_vt100_cursor(terminator)) { sink(navigation_event{*key}); } else if (auto key = detail::map_ss3_function(terminator)) { sink(function_key_event{*key}); } } }; //! Policy for decoding xterm escape sequences, including modifier parameters. export struct xterm_policy { //! Parse keyboard modifier parameters from a CSI parameter buffer. //! //! @param buffer The CSI parameter buffer string view. //! @return The parsed modifier set, or empty if none present. constexpr auto static parse_modifiers(std::string_view buffer) -> modifier_set { auto delimiter = buffer.find(';'); if (delimiter == std::string_view::npos) { return {}; } auto modifier_string = buffer.substr(delimiter + 1); auto next_delimiter = modifier_string.find(';'); if (next_delimiter != std::string_view::npos) { modifier_string = modifier_string.substr(0, next_delimiter); } auto modifier_value = 0u; for (auto character : modifier_string) { if (character >= '0' && character <= '9') { modifier_value = modifier_value * 10 + static_cast(character - '0'); } } if (modifier_value <= 1) { return {}; } return static_cast(modifier_value - 1); } //! Resolve a VT220 keypad sequence with modifiers and emit the navigation or function event. //! //! @param buffer The CSI parameter buffer containing key and modifier values. //! @param sink The event sink to receive the resolved event. constexpr auto static resolve_vt220_keypad(std::string & buffer, event_sink auto & sink) -> void { if (buffer.empty()) { return; } auto modifiers = parse_modifiers(buffer); if (auto code = detail::parse_primary_parameter(buffer)) { if (auto key = detail::map_vt220_navigation(*code)) { sink(navigation_event{*key, modifiers}); } else if (auto key = detail::map_vt220_function(*code)) { sink(function_key_event{*key, modifiers}); } } buffer.clear(); } //! Resolve a VT100 cursor sequence with modifiers and emit the navigation event. //! //! @param terminator The CSI terminator character indicating direction or key. //! @param buffer The CSI parameter buffer containing modifier values. //! @param sink The event sink to receive the resolved navigation event. constexpr auto static resolve_vt100_cursor(char terminator, std::string & buffer, event_sink auto & sink) -> void { auto modifiers = parse_modifiers(buffer); if (auto key = detail::map_vt100_cursor(terminator)) { sink(navigation_event{*key, modifiers}); } } //! Resolve an SS3 single-shift sequence and emit the corresponding navigation or function event. //! //! @param terminator The SS3 terminator character indicating direction or key. //! @param sink The event sink to receive the resolved event. constexpr auto static resolve_ss3(char terminator, event_sink auto & sink) -> void { if (auto key = detail::map_vt100_cursor(terminator)) { sink(navigation_event{*key}); } else if (auto key = detail::map_ss3_function(terminator)) { sink(function_key_event{*key}); } } }; } // namespace ttwhy::terminal