#pragma once #include #include #include #include #include #include // A terminal's screen (issue #2, N1 Q256): what a remote program's output makes of a grid of // characters. It understands what a shell, `less`, `top`, `nano` and plain `vim` send: the cursor, // erasing, sixteen colours, scroll regions, the alternate screen. No mouse. Characters are kept // as the screen's font has them (Latin-1); what it lacks becomes `?`, and box-drawing lines // become + - |. namespace roro::term { struct Cell { uint8_t ch = ' '; uint8_t attr = 0x07; // low four bits: the colour of the character, high four: of what is behind it }; class Terminal { public: Terminal(int cols, int rows, int historyLines); void feed(const uint8_t* data, size_t len); // A new size: what is on screen stays where it is, from the top left; if the cursor would fall // off the bottom, the top lines go to the history. void resize(int cols, int rows); // What the program asked to be told (where the cursor is, what kind of terminal this is). std::function reply; int cols() const { return cols_; } int rows() const { return rows_; } const Cell& cell(int row, int col) const { return grid()[static_cast(row * cols_ + col)]; } int cursorRow() const { return row_; } int cursorCol() const { return col_; } bool cursorVisible() const { return cursorShown_; } bool appCursorKeys() const { return appCursor_; } // the arrows are sent another way (vim, less) bool altScreen() const { return alt_; } uint32_t revision() const { return revision_; } // changes whenever the screen may have bool takeBell(); // Lines that scrolled off the top of the main screen, oldest first; their text only. int historyCount() const { return static_cast(history_.size()); } const std::string& historyLine(int i) const { return history_[static_cast(i)]; } std::string rowText(int row) const; // without trailing spaces private: enum class State { Ground, Esc, Csi, Osc, OscEsc, Charset }; std::vector& grid() { return alt_ ? altGrid_ : mainGrid_; } const std::vector& grid() const { return alt_ ? altGrid_ : mainGrid_; } Cell blank() const; void put(uint32_t codePoint); void control(uint8_t c); void escape(uint8_t c); void csi(uint8_t final); void sgr(); void mode(bool on); void lineFeed(); void reverseIndex(); void scrollUp(int top, int bottom, int n, bool toHistory = true); void scrollDown(int top, int bottom, int n); void eraseCells(int row, int from, int to); void moveTo(int row, int col); void reset(); int param(size_t i, int fallback) const; int cols_, rows_, historyMax_; std::vector mainGrid_, altGrid_; std::deque history_; bool alt_ = false; int row_ = 0, col_ = 0, top_ = 0, bottom_ = 0; int savedRow_ = 0, savedCol_ = 0; uint8_t savedAttr_ = 0x07; bool wrapPending_ = false, autoWrap_ = true, cursorShown_ = true, appCursor_ = false, lineDrawing_ = false, bell_ = false; uint8_t fg_ = 7, bg_ = 0; bool bold_ = false, inverse_ = false; State state_ = State::Ground; std::vector params_; bool paramStarted_ = false, private_ = false; uint32_t utf8_ = 0; int utf8Left_ = 0; uint32_t revision_ = 0; }; // What a key sends to the remote program. enum class TermKey { Char, Up, Down, Left, Right, Enter, Backspace, Tab, Escape, PageUp, PageDown }; std::string encodeKey(TermKey key, uint32_t ch, bool ctrl, bool alt, bool appCursorKeys); // One of the terminal's sixteen colours as red, green and blue. void colourRgb(int index, uint8_t& r, uint8_t& g, uint8_t& b); } // namespace roro::term