// 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 #include #include #include #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 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_