Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#pragma once
28
29#include <AK/String.h>
30#include <AK/NonnullOwnPtrVector.h>
31#include <AK/Vector.h>
32#include <LibVT/Position.h>
33
34namespace VT {
35
36class TerminalClient {
37public:
38 virtual ~TerminalClient() {}
39
40 virtual void beep() = 0;
41 virtual void set_window_title(const StringView&) = 0;
42 virtual void terminal_did_resize(u16 columns, u16 rows) = 0;
43 virtual void terminal_history_changed() = 0;
44 virtual void emit_char(u8) = 0;
45};
46
47struct Attribute {
48 Attribute() { reset(); }
49
50 static const u8 default_foreground_color = 7;
51 static const u8 default_background_color = 0;
52
53 void reset()
54 {
55 foreground_color = default_foreground_color;
56 background_color = default_background_color;
57 flags = Flags::NoAttributes;
58 }
59 u8 foreground_color;
60 u8 background_color;
61
62 enum Flags : u8 {
63 NoAttributes = 0x00,
64 Bold = 0x01,
65 Italic = 0x02,
66 Underline = 0x04,
67 Negative = 0x08,
68 Blink = 0x10,
69 Touched = 0x20,
70 };
71
72 bool is_untouched() const { return !(flags & Touched); }
73
74 // TODO: it would be really nice if we had a helper for enums that
75 // exposed bit ops for class enums...
76 u8 flags = Flags::NoAttributes;
77
78 bool operator==(const Attribute& other) const
79 {
80 return foreground_color == other.foreground_color && background_color == other.background_color && flags == other.flags;
81 }
82 bool operator!=(const Attribute& other) const
83 {
84 return !(*this == other);
85 }
86};
87
88class Terminal {
89public:
90 explicit Terminal(TerminalClient&);
91 ~Terminal();
92
93 bool m_need_full_flush { false };
94
95 void invalidate_cursor();
96 void on_char(u8);
97
98 void clear();
99 void set_size(u16 columns, u16 rows);
100 u16 columns() const { return m_columns; }
101 u16 rows() const { return m_rows; }
102
103 u16 cursor_column() const { return m_cursor_column; }
104 u16 cursor_row() const { return m_cursor_row; }
105
106 struct Line {
107 explicit Line(u16 columns);
108 ~Line();
109 void clear(Attribute);
110 bool has_only_one_background_color() const;
111 void set_length(u16);
112 StringView text() const { return { characters, m_length }; }
113
114 u8* characters { nullptr };
115 Attribute* attributes { nullptr };
116 bool dirty { false };
117 u16 m_length { 0 };
118 };
119
120 Line& line(size_t index)
121 {
122 ASSERT(index < m_rows);
123 return m_lines[index];
124 }
125 const Line& line(size_t index) const
126 {
127 ASSERT(index < m_rows);
128 return m_lines[index];
129 }
130
131 size_t max_history_size() const { return 500; }
132 const NonnullOwnPtrVector<Line>& history() const { return m_history; }
133
134 void inject_string(const StringView&);
135
136private:
137 typedef Vector<unsigned, 4> ParamVector;
138
139 void scroll_up();
140 void scroll_down();
141 void newline();
142 void set_cursor(unsigned row, unsigned column);
143 void put_character_at(unsigned row, unsigned column, u8 ch);
144 void set_window_title(const String&);
145
146 void unimplemented_escape();
147 void unimplemented_xterm_escape();
148
149 void emit_string(const StringView&);
150
151 void alter_mode(bool should_set, bool question_param, const ParamVector&);
152
153 void CUU(const ParamVector&);
154 void CUD(const ParamVector&);
155 void CUF(const ParamVector&);
156 void CUB(const ParamVector&);
157 void CUP(const ParamVector&);
158 void ED(const ParamVector&);
159 void EL(const ParamVector&);
160 void escape$M(const ParamVector&);
161 void escape$P(const ParamVector&);
162 void escape$G(const ParamVector&);
163 void escape$X(const ParamVector&);
164 void escape$b(const ParamVector&);
165 void escape$d(const ParamVector&);
166 void SGR(const ParamVector&);
167 void escape$s(const ParamVector&);
168 void escape$u(const ParamVector&);
169 void escape$t(const ParamVector&);
170 void DECSTBM(const ParamVector&);
171 void escape$S(const ParamVector&);
172 void escape$T(const ParamVector&);
173 void escape$L(const ParamVector&);
174 void RM(bool question_param, const ParamVector&);
175 void SM(bool question_param, const ParamVector&);
176 void DA(const ParamVector&);
177 void HVP(const ParamVector&);
178 void NEL();
179 void IND();
180 void RI();
181
182 TerminalClient& m_client;
183
184 NonnullOwnPtrVector<Line> m_history;
185 NonnullOwnPtrVector<Line> m_lines;
186
187 size_t m_scroll_region_top { 0 };
188 size_t m_scroll_region_bottom { 0 };
189
190 u16 m_columns { 1 };
191 u16 m_rows { 1 };
192
193 u16 m_cursor_row { 0 };
194 u16 m_cursor_column { 0 };
195 u16 m_saved_cursor_row { 0 };
196 u16 m_saved_cursor_column { 0 };
197 bool m_swallow_current { false };
198 bool m_stomp { false };
199
200 Attribute m_current_attribute;
201
202 void execute_escape_sequence(u8 final);
203 void execute_xterm_command();
204 void execute_hashtag(u8);
205
206 enum EscapeState {
207 Normal,
208 GotEscape,
209 ExpectParameter,
210 ExpectIntermediate,
211 ExpectFinal,
212 ExpectHashtagDigit,
213 ExpectXtermParameter1,
214 ExpectXtermParameter2,
215 ExpectXtermFinal,
216 };
217 EscapeState m_escape_state { Normal };
218 Vector<u8> m_parameters;
219 Vector<u8> m_intermediates;
220 Vector<u8> m_xterm_param1;
221 Vector<u8> m_xterm_param2;
222 Vector<bool> m_horizontal_tabs;
223 u8 m_final { 0 };
224
225 u8 m_last_char { 0 };
226};
227
228}