module; #include #include #include #include #include export module ttwhy:scoped_attributes; namespace ttwhy { auto static read_attributes(int file_descriptor) -> termios { auto active_attributes = termios{}; if (::tcgetattr(file_descriptor, &active_attributes)) { throw std::system_error(errno, std::system_category(), "failed to read termios attributes"); } return active_attributes; } auto static get_line_discipline_flag(int file_descriptor, int flag) -> bool { auto active_attributes = read_attributes(file_descriptor); return active_attributes.c_lflag & flag; } auto static set_line_discipline_flag(int file_descriptor, int flag, bool enabled) -> void { auto active_attributes = read_attributes(file_descriptor); if ((active_attributes.c_lflag & flag) == enabled) { return; } if (enabled) { active_attributes.c_lflag = active_attributes.c_lflag | flag; } else { active_attributes.c_lflag = active_attributes.c_lflag & ~flag; } if (::tcsetattr(file_descriptor, TCSANOW, &active_attributes)) { throw std::system_error(errno, std::system_category(), "failed to write termios attributes"); } } //! Store the current TC attributes and restore them on destruction. export struct scoped_attributes { explicit scoped_attributes(int file_descriptor) : m_file_descriptor{::dup(file_descriptor)} , m_attributes{read_attributes(file_descriptor)} {} scoped_attributes(scoped_attributes const &) = delete; scoped_attributes(scoped_attributes && other) : m_file_descriptor{std::exchange(other.m_file_descriptor, -1)} , m_attributes{std::move(other.m_attributes)} {} ~scoped_attributes() { if (m_file_descriptor > 0) { ::tcsetattr(m_file_descriptor, TCSANOW, &m_attributes); ::close(m_file_descriptor); } } auto operator=(scoped_attributes const &) -> scoped_attributes & = delete; auto operator=(scoped_attributes && other) -> scoped_attributes & { std::ranges::swap(m_file_descriptor, other.m_file_descriptor); std::ranges::swap(m_attributes, other.m_attributes); return *this; } //! Check if canonical mode is currently active [[nodiscard]] auto canonical_mode() const noexcept -> bool { return get_line_discipline_flag(m_file_descriptor, ICANON); } //! Set whether canonical mode is active or not. auto canonical_mode(bool enabled) & -> scoped_attributes & { set_line_discipline_flag(m_file_descriptor, ICANON, enabled); return *this; } auto canonical_mode(bool enabled) && -> scoped_attributes { set_line_discipline_flag(m_file_descriptor, ICANON, enabled); return std::move(*this); } [[nodiscard]] auto echo() const noexcept -> bool { return get_line_discipline_flag(m_file_descriptor, ECHO); } auto echo(bool enabled) & -> scoped_attributes & { set_line_discipline_flag(m_file_descriptor, ECHO, enabled); return *this; } auto echo(bool enabled) && -> scoped_attributes { set_line_discipline_flag(m_file_descriptor, ECHO, enabled); return std::move(*this); } private: int m_file_descriptor; termios m_attributes; }; } // namespace ttwhy