aboutsummaryrefslogtreecommitdiff
path: root/ttwhy/io.cppm
blob: 5fd5e50e7e1e705cd34fa59b9e31c100207e5c26 (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
130
module;

#include <asio.hpp>
#include <asio/experimental/awaitable_operators.hpp>

#include <chrono>
#include <format>
#include <span>
#include <vector>

export module ttwhy:io;

import ttwhy.scanners;

namespace ttwhy::io
{

  export template<typename Stream, typename TerminalAttributes>
  auto handle_signals(Stream & stream, TerminalAttributes & attributes) -> asio::awaitable<std::error_code>
  {
    auto executor = co_await asio::this_coro::executor;
    auto signals = asio::signal_set{executor, SIGINT, SIGTERM};
    signals.add(SIGTSTP);
    signals.add(SIGCONT);

    while (true)
    {
      auto [error, signal] = co_await signals.async_wait(asio::as_tuple(asio::use_awaitable));
      if (error)
      {
        co_return error;
      }

      switch (signal)
      {
        case SIGINT:
        case SIGTERM:
        {
          auto message = std::format("Received signal {}, exiting ...\n", signal);
          co_await asio::async_write(stream, asio::buffer(message), asio::use_awaitable);
          co_return asio::error_code{};
        }
        case SIGTSTP:
        {
          attributes.canonical_mode(true).echo(true);
          ::raise(SIGSTOP);
          break;
        }
        case SIGCONT:
        {
          attributes.canonical_mode(false).echo(false);
          break;
        }
      }

      if (!error)
      {
      }
    }
  }

  export template<typename InputStream, typename AppRouter>
  auto read_events(InputStream & stream, AppRouter & router) -> asio::awaitable<void>
  {
    using namespace asio::experimental::awaitable_operators;
    using namespace std::chrono_literals;

    auto executor = co_await asio::this_coro::executor;
    auto timer = asio::steady_timer{executor};

    auto queue = std::vector<scanners::input_event>{};
    queue.reserve(16);

    auto scanner = scanners::ansi{queue};
    auto raw_buffer = std::array<char, 64>{};

    while (true)
    {
      auto error = asio::error_code{};
      auto bytes_read = std::size_t{};

      if (scanner.is_pending())
      {
        timer.expires_after(500ms);

        auto result = co_await (stream.async_read_some(asio::buffer(raw_buffer), asio::as_tuple(asio::use_awaitable)) ||
                                timer.async_wait(asio::as_tuple(asio::use_awaitable)));

        if (result.index() == 0)
        {
          std::tie(error, bytes_read) = std::get<0>(result);
        }
        else
        {
          scanner.timeout();
          for (auto const & event : queue)
          {
            co_await router.process(event);
          }
          queue.clear();
          continue;
        }
      }
      else
      {
        std::tie(error, bytes_read) =
            co_await stream.async_read_some(asio::buffer(raw_buffer), asio::as_tuple(asio::use_awaitable));
      }

      if (error)
      {
        if (error == asio::error::interrupted)
        {
          continue;
        }
        co_return;
      }

      auto const byte_span = std::span<char const>{raw_buffer.data(), bytes_read};
      scanner.process(byte_span);

      for (auto const & event : queue)
      {
        co_await router.process(event);
      }
      queue.clear();
    }
  }

}  // namespace ttwhy::io