aboutsummaryrefslogtreecommitdiff
path: root/ttwhy/terminal/readers.cppm
blob: ffa56871cf7d20e65e7dc440d6fc408f5e7ba401 (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
module;

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

#include <array>
#include <chrono>
#include <span>
#include <vector>

export module ttwhy.terminal:readers;

import :events;
import :policies;
import :scanner;
import ttwhy.core;

namespace ttwhy::terminal
{

  export template<typename TerminalPolicy = xterm_policy, typename InputStream, ttwhy::router<input_event> 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<input_event>{};
    queue.reserve(16);

    auto sink = [&queue](auto const & event) {
      queue.push_back(event);
    };

    auto scanner = terminal::scanner<decltype(sink), TerminalPolicy>{sink};

    auto raw_buffer = std::array<char, 64>{};

    while (true)
    {
      auto error = asio::error_code{};
      auto bytes_read = 0uz;

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

        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::terminal