aboutsummaryrefslogtreecommitdiff
path: root/ttwhy/scoped_attributes.cppm
blob: 243d4acef31798e3740c494af84f4c3d9e5d9908 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
module;

#include <termios.h>
#include <unistd.h>

#include <cerrno>
#include <system_error>
#include <utility>

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