diff options
-rw-r--r-- | Libraries/LibVT/CMakeLists.txt | 1 | ||||
-rw-r--r-- | Libraries/LibVT/Line.cpp | 95 | ||||
-rw-r--r-- | Libraries/LibVT/Line.h | 111 | ||||
-rw-r--r-- | Libraries/LibVT/Terminal.cpp | 86 | ||||
-rw-r--r-- | Libraries/LibVT/Terminal.h | 66 | ||||
-rw-r--r-- | Libraries/LibVT/TerminalWidget.cpp | 22 |
6 files changed, 232 insertions, 149 deletions
diff --git a/Libraries/LibVT/CMakeLists.txt b/Libraries/LibVT/CMakeLists.txt index 78f0772731..eeca322d28 100644 --- a/Libraries/LibVT/CMakeLists.txt +++ b/Libraries/LibVT/CMakeLists.txt @@ -1,4 +1,5 @@ set(SOURCES + Line.cpp Terminal.cpp TerminalWidget.cpp ) diff --git a/Libraries/LibVT/Line.cpp b/Libraries/LibVT/Line.cpp new file mode 100644 index 0000000000..1760e2b901 --- /dev/null +++ b/Libraries/LibVT/Line.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <LibVT/Line.h> +#include <string.h> + +namespace VT { + +Line::Line(u16 length) +{ + set_length(length); +} + +Line::~Line() +{ + delete[] m_characters; + delete[] m_attributes; +} + +void Line::set_length(u16 new_length) +{ + if (m_length == new_length) + return; + auto* new_characters = new u8[new_length]; + auto* new_attributes = new Attribute[new_length]; + memset(new_characters, ' ', new_length); + if (m_characters && m_attributes) { + memcpy(new_characters, m_characters, min(m_length, new_length)); + for (size_t i = 0; i < min(m_length, new_length); ++i) + new_attributes[i] = m_attributes[i]; + } + delete[] m_characters; + delete[] m_attributes; + m_characters = new_characters; + m_attributes = new_attributes; + m_length = new_length; +} + +void Line::clear(Attribute attribute) +{ + if (m_dirty) { + memset(m_characters, ' ', m_length); + for (u16 i = 0; i < m_length; ++i) + m_attributes[i] = attribute; + return; + } + for (unsigned i = 0; i < m_length; ++i) { + if (m_characters[i] != ' ') + m_dirty = true; + m_characters[i] = ' '; + } + for (unsigned i = 0; i < m_length; ++i) { + if (m_attributes[i] != attribute) + m_dirty = true; + m_attributes[i] = attribute; + } +} + +bool Line::has_only_one_background_color() const +{ + if (!m_length) + return true; + // FIXME: Cache this result? + auto color = m_attributes[0].background_color; + for (size_t i = 1; i < m_length; ++i) { + if (m_attributes[i].background_color != color) + return false; + } + return true; +} + +} diff --git a/Libraries/LibVT/Line.h b/Libraries/LibVT/Line.h new file mode 100644 index 0000000000..69e7762e1d --- /dev/null +++ b/Libraries/LibVT/Line.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include <AK/Noncopyable.h> +#include <AK/String.h> +#include <LibVT/XtermColors.h> + +namespace VT { + +struct Attribute { + Attribute() { reset(); } + + static const u32 default_foreground_color = xterm_colors[7]; + static const u32 default_background_color = xterm_colors[0]; + + void reset() + { + foreground_color = default_foreground_color; + background_color = default_background_color; + flags = Flags::NoAttributes; + } + u32 foreground_color; + u32 background_color; + + String href; + String href_id; + + enum Flags : u8 { + NoAttributes = 0x00, + Bold = 0x01, + Italic = 0x02, + Underline = 0x04, + Negative = 0x08, + Blink = 0x10, + Touched = 0x20, + }; + + bool is_untouched() const { return !(flags & Touched); } + + // TODO: it would be really nice if we had a helper for enums that + // exposed bit ops for class enums... + u8 flags = Flags::NoAttributes; + + bool operator==(const Attribute& other) const + { + return foreground_color == other.foreground_color && background_color == other.background_color && flags == other.flags; + } + bool operator!=(const Attribute& other) const + { + return !(*this == other); + } +}; + +class Line { + AK_MAKE_NONCOPYABLE(Line); + AK_MAKE_NONMOVABLE(Line); + +public: + explicit Line(u16 columns); + ~Line(); + + void clear(Attribute); + bool has_only_one_background_color() const; + void set_length(u16); + + StringView text() const { return { m_characters, m_length }; } + + u16 length() const { return m_length; } + + const u8* characters() const { return m_characters; } + u8* characters() { return m_characters; } + + bool is_dirty() const { return m_dirty; } + void set_dirty(bool b) { m_dirty = b; } + + const Attribute* attributes() const { return m_attributes; } + Attribute* attributes() { return m_attributes; } + +private: + u8* m_characters { nullptr }; + Attribute* m_attributes { nullptr }; + bool m_dirty { false }; + u16 m_length { 0 }; +}; + +} diff --git a/Libraries/LibVT/Terminal.cpp b/Libraries/LibVT/Terminal.cpp index 608f6016b9..bd6b5f4274 100644 --- a/Libraries/LibVT/Terminal.cpp +++ b/Libraries/LibVT/Terminal.cpp @@ -41,68 +41,6 @@ Terminal::~Terminal() { } -Terminal::Line::Line(u16 length) -{ - set_length(length); -} - -Terminal::Line::~Line() -{ - delete[] characters; - delete[] attributes; -} - -void Terminal::Line::set_length(u16 new_length) -{ - if (m_length == new_length) - return; - auto* new_characters = new u8[new_length]; - auto* new_attributes = new Attribute[new_length]; - memset(new_characters, ' ', new_length); - if (characters && attributes) { - memcpy(new_characters, characters, min(m_length, new_length)); - for (size_t i = 0; i < min(m_length, new_length); ++i) - new_attributes[i] = attributes[i]; - } - delete[] characters; - delete[] attributes; - characters = new_characters; - attributes = new_attributes; - m_length = new_length; -} - -void Terminal::Line::clear(Attribute attribute) -{ - if (dirty) { - memset(characters, ' ', m_length); - for (u16 i = 0; i < m_length; ++i) - attributes[i] = attribute; - return; - } - for (unsigned i = 0; i < m_length; ++i) { - if (characters[i] != ' ') - dirty = true; - characters[i] = ' '; - } - for (unsigned i = 0; i < m_length; ++i) { - if (attributes[i] != attribute) - dirty = true; - attributes[i] = attribute; - } -} - -bool Terminal::Line::has_only_one_background_color() const -{ - if (!m_length) - return true; - // FIXME: Cache this result? - auto color = attributes[0].background_color; - for (size_t i = 1; i < m_length; ++i) { - if (attributes[i].background_color != color) - return false; - } - return true; -} void Terminal::clear() { @@ -590,14 +528,14 @@ void Terminal::escape$P(const ParamVector& params) auto& line = m_lines[m_cursor_row]; // Move n characters of line to the left - for (int i = m_cursor_column; i < line.m_length - num; i++) - line.characters[i] = line.characters[i + num]; + for (int i = m_cursor_column; i < line.length() - num; i++) + line.characters()[i] = line.characters()[i + num]; // Fill remainder of line with blanks - for (int i = line.m_length - num; i < line.m_length; i++) - line.characters[i] = ' '; + for (int i = line.length() - num; i < line.length(); i++) + line.characters()[i] = ' '; - line.dirty = true; + line.set_dirty(true); } void Terminal::execute_xterm_command() @@ -828,10 +766,10 @@ void Terminal::put_character_at(unsigned row, unsigned column, u8 ch) ASSERT(row < rows()); ASSERT(column < columns()); auto& line = m_lines[row]; - line.characters[column] = ch; - line.attributes[column] = m_current_attribute; - line.attributes[column].flags |= Attribute::Touched; - line.dirty = true; + line.characters()[column] = ch; + line.attributes()[column] = m_current_attribute; + line.attributes()[column].flags |= Attribute::Touched; + line.set_dirty(true); m_last_char = ch; } @@ -1087,7 +1025,7 @@ void Terminal::set_size(u16 columns, u16 rows) void Terminal::invalidate_cursor() { - m_lines[m_cursor_row].dirty = true; + m_lines[m_cursor_row].set_dirty(true); } void Terminal::execute_hashtag(u8 hashtag) @@ -1113,9 +1051,9 @@ Attribute Terminal::attribute_at(const Position& position) const if (position.row() >= static_cast<int>(line_count())) return {}; auto& line = this->line(position.row()); - if (position.column() >= line.m_length) + if (position.column() >= line.length()) return {}; - return line.attributes[position.column()]; + return line.attributes()[position.column()]; } } diff --git a/Libraries/LibVT/Terminal.h b/Libraries/LibVT/Terminal.h index 38f42dcdb8..8e11041ac9 100644 --- a/Libraries/LibVT/Terminal.h +++ b/Libraries/LibVT/Terminal.h @@ -30,14 +30,14 @@ #include <AK/NonnullOwnPtrVector.h> #include <AK/String.h> #include <AK/Vector.h> +#include <LibVT/Line.h> #include <LibVT/Position.h> -#include <LibVT/XtermColors.h> namespace VT { class TerminalClient { public: - virtual ~TerminalClient() {} + virtual ~TerminalClient() { } virtual void beep() = 0; virtual void set_window_title(const StringView&) = 0; @@ -46,50 +46,6 @@ public: virtual void emit(const u8*, size_t) = 0; }; -struct Attribute { - Attribute() { reset(); } - - static const u32 default_foreground_color = xterm_colors[7]; - static const u32 default_background_color = xterm_colors[0]; - - void reset() - { - foreground_color = default_foreground_color; - background_color = default_background_color; - flags = Flags::NoAttributes; - } - u32 foreground_color; - u32 background_color; - - String href; - String href_id; - - enum Flags : u8 { - NoAttributes = 0x00, - Bold = 0x01, - Italic = 0x02, - Underline = 0x04, - Negative = 0x08, - Blink = 0x10, - Touched = 0x20, - }; - - bool is_untouched() const { return !(flags & Touched); } - - // TODO: it would be really nice if we had a helper for enums that - // exposed bit ops for class enums... - u8 flags = Flags::NoAttributes; - - bool operator==(const Attribute& other) const - { - return foreground_color == other.foreground_color && background_color == other.background_color && flags == other.flags; - } - bool operator!=(const Attribute& other) const - { - return !(*this == other); - } -}; - class Terminal { public: explicit Terminal(TerminalClient&); @@ -108,24 +64,6 @@ public: u16 cursor_column() const { return m_cursor_column; } u16 cursor_row() const { return m_cursor_row; } - struct Line { - AK_MAKE_NONCOPYABLE(Line); - AK_MAKE_NONMOVABLE(Line); - - public: - explicit Line(u16 columns); - ~Line(); - void clear(Attribute); - bool has_only_one_background_color() const; - void set_length(u16); - StringView text() const { return { characters, m_length }; } - - u8* characters { nullptr }; - Attribute* attributes { nullptr }; - bool dirty { false }; - u16 m_length { 0 }; - }; - size_t line_count() const { return m_history.size() + m_lines.size(); diff --git a/Libraries/LibVT/TerminalWidget.cpp b/Libraries/LibVT/TerminalWidget.cpp index e076e0c30d..5e8af42ccc 100644 --- a/Libraries/LibVT/TerminalWidget.cpp +++ b/Libraries/LibVT/TerminalWidget.cpp @@ -336,7 +336,7 @@ void TerminalWidget::paint_event(GUI::PaintEvent& event) if (m_visual_beep_timer->is_active()) painter.clear_rect(row_rect, Color::Red); else if (has_only_one_background_color) - painter.clear_rect(row_rect, color_from_rgb(line.attributes[0].background_color).with_alpha(m_opacity)); + painter.clear_rect(row_rect, color_from_rgb(line.attributes()[0].background_color).with_alpha(m_opacity)); // The terminal insists on thinking characters and // bytes are the same thing. We want to still draw @@ -369,7 +369,7 @@ void TerminalWidget::paint_event(GUI::PaintEvent& event) && visual_row == row_with_cursor && column == m_terminal.cursor_column(); should_reverse_fill_for_cursor_or_selection |= selection_contains({ first_row_from_history + visual_row, column }); - attribute = line.attributes[column]; + attribute = line.attributes()[column]; text_color = color_from_rgb(should_reverse_fill_for_cursor_or_selection ? attribute.background_color : attribute.foreground_color); auto character_rect = glyph_rect(visual_row, column); auto cell_rect = character_rect.inflated(0, m_line_spacing); @@ -430,7 +430,7 @@ void TerminalWidget::paint_event(GUI::PaintEvent& event) auto& cursor_line = m_terminal.line(first_row_from_history + row_with_cursor); if (m_terminal.cursor_row() < (m_terminal.rows() - rows_from_history)) { auto cell_rect = glyph_rect(row_with_cursor, m_terminal.cursor_column()).inflated(0, m_line_spacing); - painter.draw_rect(cell_rect, color_from_rgb(cursor_line.attributes[m_terminal.cursor_column()].foreground_color)); + painter.draw_rect(cell_rect, color_from_rgb(cursor_line.attributes()[m_terminal.cursor_column()].foreground_color)); } } } @@ -456,9 +456,9 @@ void TerminalWidget::flush_dirty_lines() } Gfx::Rect rect; for (int i = 0; i < m_terminal.rows(); ++i) { - if (m_terminal.visible_line(i).dirty) { + if (m_terminal.visible_line(i).is_dirty()) { rect = rect.united(row_rect(i)); - m_terminal.visible_line(i).dirty = false; + m_terminal.visible_line(i).set_dirty(false); } } update(rect); @@ -583,16 +583,16 @@ void TerminalWidget::doubleclick_event(GUI::MouseEvent& event) auto position = buffer_position_at(event.position()); auto& line = m_terminal.line(position.row()); - bool want_whitespace = line.characters[position.column()] == ' '; + bool want_whitespace = line.characters()[position.column()] == ' '; int start_column = 0; int end_column = 0; - for (int column = position.column(); column >= 0 && (line.characters[column] == ' ') == want_whitespace; --column) { + for (int column = position.column(); column >= 0 && (line.characters()[column] == ' ') == want_whitespace; --column) { start_column = column; } - for (int column = position.column(); column < m_terminal.columns() && (line.characters[column] == ' ') == want_whitespace; ++column) { + for (int column = position.column(); column < m_terminal.columns() && (line.characters()[column] == ' ') == want_whitespace; ++column) { end_column = column; } @@ -758,12 +758,12 @@ String TerminalWidget::selected_text() const int last_column = last_selection_column_on_row(row); for (int column = first_column; column <= last_column; ++column) { auto& line = m_terminal.line(row); - if (line.attributes[column].is_untouched()) { + if (line.attributes()[column].is_untouched()) { builder.append('\n'); break; } - builder.append(line.characters[column]); - if (column == line.m_length - 1 || (m_rectangle_selection && column == last_column)) { + builder.append(line.characters()[column]); + if (column == line.length() - 1 || (m_rectangle_selection && column == last_column)) { builder.append('\n'); } } |