aboutsummaryrefslogtreecommitdiff
path: root/src/ui/terminal/line.h
blob: fc0ee73cac5bec71b32264f8e266bf7690315817 (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
// Copyright 2021 Benjamin Barenblat
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

// A terminal driver for single-line UIs.

#ifndef EC_SRC_UI_TERMINAL_LINE_H_
#define EC_SRC_UI_TERMINAL_LINE_H_

#include <termios.h>

#include <memory>
#include <thread>
#include <utility>

#include "goldfishterm/simple.h"
#include "third_party/abseil/absl/base/thread_annotations.h"
#include "third_party/abseil/absl/strings/string_view.h"
#include "third_party/abseil/absl/synchronization/mutex.h"

namespace ec {

// The driver itself.
//
// This class is thread-safe, but there are still some sharp edges. See the
// warning in the constructor documentation about additional steps required to
// use this class in a multi-threaded program.
class TerminalLine final {
 public:
  // Starts driving the standard input and standard output of the process.
  //
  // Multi-threading warning: You must block SIGWINCH in all your program's
  // threads before constructing an instance of this class. This class detects
  // failure to block SIGWINCH in the calling thread, but it cannot check all
  // the threads. It is your responsibility to block SIGWINCH everywhere!
  // (BlockSigwinch is a convenience function to do this in the current thread.)
  explicit TerminalLine();

  TerminalLine(const TerminalLine&) = delete;
  TerminalLine& operator=(const TerminalLine&) = delete;

  ~TerminalLine() noexcept;

  void SetLine(std::string) ABSL_LOCKS_EXCLUDED(mu_);
  void Refresh() ABSL_LOCKS_EXCLUDED(mu_);

  void SetLineImmediately(std::string text) {
    SetLine(std::move(text));
    Refresh();
  }

  char GetChar();
  char interrupt_char() const noexcept { return current_termios_.c_cc[VINTR]; }
  char quit_char() const noexcept { return current_termios_.c_cc[VQUIT]; }
  char suspend_char() const noexcept { return current_termios_.c_cc[VSUSP]; }
#ifdef VDSUSP
  char delayed_suspend_char() const noexcept {
    return current_termios_.c_cc[VDSUSP];
  }
#endif

  void Beep() ABSL_LOCKS_EXCLUDED(mu_);

  // Prints the specified message on a new line, and redisplays the original
  // text on the line after that.
  void PrintLine(absl::string_view) ABSL_LOCKS_EXCLUDED(mu_);

 private:
  void EnterRawMode();
  void ExitRawMode() noexcept;

  void ReportSigwinch() ABSL_LOCKS_EXCLUDED(mu_);

  void WriteRaw(absl::string_view bytes) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_);

  termios original_termios_, current_termios_;
  std::unique_ptr<goldfishterm::SimpleTerminalOutput> tty_;

  std::thread sigwinch_watcher_;

  absl::Mutex mu_;
  int columns_ ABSL_GUARDED_BY(mu_);
  std::string line_ ABSL_GUARDED_BY(mu_);
};

// Names for control characters.
constexpr char kControlD = '\x04';
constexpr char kControlU = '\x15';

// A convenience function to block SIGWINCH in the calling thread.
void BlockSigwinch();

}  // namespace ec

#endif  // EC_SRC_UI_TERMINAL_LINE_H_