ソースを参照

LibVT: Make VT::Line use a Vector for storage

This is preparation for non-destructive terminal resizing which will
require more dynamic storage for lines.
Andreas Kling 4 年 前
コミット
b7c66233f6

+ 1 - 1
Kernel/TTY/VirtualConsole.cpp

@@ -293,7 +293,7 @@ void VirtualConsole::flush_dirty_lines()
             continue;
         for (size_t column = 0; column < line.length(); ++column) {
             u32 code_point = line.code_point(column);
-            auto attribute = line.attributes()[column];
+            auto attribute = line.attribute_at(column);
             u16 vga_index = (visual_row * 160) + (column * 2);
             m_current_vga_window[vga_index] = code_point < 128 ? code_point : '?';
             m_current_vga_window[vga_index + 1] = attribute_to_vga(attribute);

+ 16 - 67
Userland/Libraries/LibVT/Line.cpp

@@ -25,103 +25,52 @@
  */
 
 #include <LibVT/Line.h>
-#include <string.h>
 
 namespace VT {
 
-Line::Line(u16 length)
+Line::Line(size_t length)
 {
     set_length(length);
 }
 
 Line::~Line()
 {
-    if (m_utf32)
-        delete[] m_code_points.as_u32;
-    else
-        delete[] m_code_points.as_u8;
-    delete[] m_attributes;
 }
 
-template<typename CodepointType>
-static CodepointType* create_new_code_point_array(size_t new_length, const CodepointType* old_code_points, size_t old_length)
+void Line::set_length(size_t new_length)
 {
-    auto* new_code_points = new CodepointType[new_length];
-    for (size_t i = 0; i < new_length; ++i)
-        new_code_points[i] = ' ';
-    if (old_code_points) {
-        for (size_t i = 0; i < min(old_length, new_length); ++i) {
-            new_code_points[i] = old_code_points[i];
-        }
-    }
-    delete[] old_code_points;
-    return new_code_points;
-}
-
-void Line::set_length(u16 new_length)
-{
-    if (m_length == new_length)
+    size_t old_length = length();
+    if (old_length == new_length)
         return;
-
-    if (m_utf32)
-        m_code_points.as_u32 = create_new_code_point_array<u32>(new_length, m_code_points.as_u32, m_length);
-    else
-        m_code_points.as_u8 = create_new_code_point_array<u8>(new_length, m_code_points.as_u8, m_length);
-
-    auto* new_attributes = new Attribute[new_length];
-    if (m_attributes) {
-        for (size_t i = 0; i < min(m_length, new_length); ++i)
-            new_attributes[i] = m_attributes[i];
-    }
-    delete[] m_attributes;
-    m_attributes = new_attributes;
-    m_length = new_length;
+    m_cells.resize(new_length);
 }
 
-void Line::clear(Attribute attribute)
+void Line::clear(const Attribute& attribute)
 {
     if (m_dirty) {
-        for (u16 i = 0; i < m_length; ++i) {
-            set_code_point(i, ' ');
-            m_attributes[i] = attribute;
+        for (auto& cell : m_cells) {
+            cell = Cell { .code_point = ' ', .attribute = attribute };
         }
         return;
     }
-    for (unsigned i = 0; i < m_length; ++i) {
-        if (code_point(i) != ' ')
-            m_dirty = true;
-        set_code_point(i, ' ');
-    }
-    for (unsigned i = 0; i < m_length; ++i) {
-        if (m_attributes[i] != attribute)
-            m_dirty = true;
-        m_attributes[i] = attribute;
+    for (auto& cell : m_cells) {
+        if (!m_dirty)
+            m_dirty = cell.code_point != ' ' || cell.attribute != attribute;
+        cell = Cell { .code_point = ' ', .attribute = attribute };
     }
 }
 
 bool Line::has_only_one_background_color() const
 {
-    if (!m_length)
+    if (!length())
         return true;
     // FIXME: Cache this result?
-    auto color = m_attributes[0].effective_background_color();
-    for (size_t i = 1; i < m_length; ++i) {
-        if (m_attributes[i].effective_background_color() != color)
+    auto color = attribute_at(0).effective_background_color();
+    for (size_t i = 1; i < length(); ++i) {
+        if (attribute_at(i).effective_background_color() != color)
             return false;
     }
     return true;
 }
 
-void Line::convert_to_utf32()
-{
-    VERIFY(!m_utf32);
-    auto* new_code_points = new u32[m_length];
-    for (size_t i = 0; i < m_length; ++i) {
-        new_code_points[i] = m_code_points.as_u8[i];
-    }
-    delete m_code_points.as_u8;
-    m_code_points.as_u32 = new_code_points;
-    m_utf32 = true;
-}
-
 }

+ 21 - 30
Userland/Libraries/LibVT/Line.h

@@ -28,6 +28,7 @@
 
 #include <AK/Noncopyable.h>
 #include <AK/String.h>
+#include <AK/Vector.h>
 #include <LibVT/XtermColors.h>
 
 namespace VT {
@@ -44,8 +45,8 @@ struct Attribute {
         background_color = default_background_color;
         flags = Flags::NoAttributes;
     }
-    u32 foreground_color;
-    u32 background_color;
+    u32 foreground_color {};
+    u32 background_color {};
 
     u32 effective_background_color() const { return flags & Negative ? foreground_color : background_color; }
     u32 effective_foreground_color() const { return flags & Negative ? background_color : foreground_color; }
@@ -84,52 +85,42 @@ class Line {
     AK_MAKE_NONMOVABLE(Line);
 
 public:
-    explicit Line(u16 columns);
+    explicit Line(size_t length);
     ~Line();
 
-    void clear(Attribute);
+    struct Cell {
+        u32 code_point {};
+        Attribute attribute;
+    };
+
+    const Attribute& attribute_at(size_t index) const { return m_cells[index].attribute; }
+    Attribute& attribute_at(size_t index) { return m_cells[index].attribute; }
+
+    Cell& cell_at(size_t index) { return m_cells[index]; }
+    const Cell& cell_at(size_t index) const { return m_cells[index]; }
+
+    void clear(const Attribute&);
     bool has_only_one_background_color() const;
-    void set_length(u16);
 
-    u16 length() const { return m_length; }
+    size_t length() const { return m_cells.size(); }
+    void set_length(size_t);
 
     u32 code_point(size_t index) const
     {
-        if (m_utf32)
-            return m_code_points.as_u32[index];
-        return m_code_points.as_u8[index];
+        return m_cells[index].code_point;
     }
 
     void set_code_point(size_t index, u32 code_point)
     {
-        if (!m_utf32 && code_point & 0xffffff80u)
-            convert_to_utf32();
-
-        if (m_utf32)
-            m_code_points.as_u32[index] = code_point;
-        else
-            m_code_points.as_u8[index] = code_point;
+        m_cells[index].code_point = code_point;
     }
 
     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; }
-
-    void convert_to_utf32();
-
-    bool is_utf32() const { return m_utf32; }
-
 private:
-    union {
-        u8* as_u8;
-        u32* as_u32;
-    } m_code_points { nullptr };
-    Attribute* m_attributes { nullptr };
+    Vector<Cell> m_cells;
     bool m_dirty { false };
-    bool m_utf32 { false };
-    u16 m_length { 0 };
 };
 
 }

+ 6 - 7
Userland/Libraries/LibVT/Terminal.cpp

@@ -28,7 +28,6 @@
 #include <AK/StringBuilder.h>
 #include <AK/StringView.h>
 #include <LibVT/Terminal.h>
-#include <string.h>
 
 namespace VT {
 
@@ -535,11 +534,11 @@ void Terminal::DCH(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.length() - num; i++)
+    for (size_t i = m_cursor_column; i < line.length() - num; i++)
         line.set_code_point(i, line.code_point(i + num));
 
     // Fill remainder of line with blanks
-    for (int i = line.length() - num; i < line.length(); i++)
+    for (size_t i = line.length() - num; i < line.length(); i++)
         line.set_code_point(i, ' ');
 
     line.set_dirty(true);
@@ -759,8 +758,8 @@ void Terminal::put_character_at(unsigned row, unsigned column, u32 code_point)
     VERIFY(column < columns());
     auto& line = m_lines[row];
     line.set_code_point(column, code_point);
-    line.attributes()[column] = m_current_attribute;
-    line.attributes()[column].flags |= Attribute::Touched;
+    line.attribute_at(column) = m_current_attribute;
+    line.attribute_at(column).flags |= Attribute::Touched;
     line.set_dirty(true);
 
     m_last_code_point = code_point;
@@ -1191,9 +1190,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.length())
+    if (static_cast<size_t>(position.column()) >= line.length())
         return {};
-    return line.attributes()[position.column()];
+    return line.attribute_at(position.column());
 }
 
 }

+ 15 - 17
Userland/Libraries/LibVT/TerminalWidget.cpp

@@ -313,7 +313,7 @@ void TerminalWidget::paint_event(GUI::PaintEvent& event)
         for (u16 visual_row = 0; visual_row < m_terminal.rows(); ++visual_row) {
             auto& line = m_terminal.line(first_row_from_history + visual_row);
             for (size_t column = 0; column < line.length(); ++column) {
-                if (m_hovered_href_id == line.attributes()[column].href_id) {
+                if (m_hovered_href_id == line.attribute_at(column).href_id) {
                     bool merged_with_existing_rect = false;
                     auto glyph_rect = this->glyph_rect(visual_row, column);
                     for (auto& rect : hovered_href_rects) {
@@ -340,7 +340,7 @@ void TerminalWidget::paint_event(GUI::PaintEvent& event)
         if (visual_beep_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].effective_background_color()).with_alpha(m_opacity));
+            painter.clear_rect(row_rect, color_from_rgb(line.attribute_at(0).effective_background_color()).with_alpha(m_opacity));
 
         for (size_t column = 0; column < line.length(); ++column) {
             bool should_reverse_fill_for_cursor_or_selection = m_cursor_blink_state
@@ -348,7 +348,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, (int)column });
-            auto attribute = line.attributes()[column];
+            auto attribute = line.attribute_at(column);
             auto character_rect = glyph_rect(visual_row, column);
             auto cell_rect = character_rect.inflated(0, m_line_spacing);
             auto text_color = color_from_rgb(should_reverse_fill_for_cursor_or_selection ? attribute.effective_background_color() : attribute.effective_foreground_color());
@@ -405,7 +405,7 @@ void TerminalWidget::paint_event(GUI::PaintEvent& event)
             continue;
         auto& line = m_terminal.line(first_row_from_history + visual_row);
         for (size_t column = 0; column < line.length(); ++column) {
-            auto attribute = line.attributes()[column];
+            auto attribute = line.attribute_at(column);
             bool should_reverse_fill_for_cursor_or_selection = m_cursor_blink_state
                 && m_has_logical_focus
                 && visual_row == row_with_cursor
@@ -436,7 +436,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()].effective_foreground_color()));
+            painter.draw_rect(cell_rect, color_from_rgb(cursor_line.attribute_at(m_terminal.cursor_column()).effective_foreground_color()));
         }
     }
 }
@@ -590,18 +590,20 @@ VT::Position TerminalWidget::buffer_position_at(const Gfx::IntPoint& position) c
 
 u32 TerminalWidget::code_point_at(const VT::Position& position) const
 {
+    VERIFY(position.is_valid());
     VERIFY(position.row() >= 0 && static_cast<size_t>(position.row()) < m_terminal.line_count());
     auto& line = m_terminal.line(position.row());
-    if (position.column() == line.length())
+    if (static_cast<size_t>(position.column()) == line.length())
         return '\n';
     return line.code_point(position.column());
 }
 
 VT::Position TerminalWidget::next_position_after(const VT::Position& position, bool should_wrap) const
 {
+    VERIFY(position.is_valid());
     VERIFY(position.row() >= 0 && static_cast<size_t>(position.row()) < m_terminal.line_count());
     auto& line = m_terminal.line(position.row());
-    if (position.column() == line.length()) {
+    if (static_cast<size_t>(position.column()) == line.length()) {
         if (static_cast<size_t>(position.row()) == m_terminal.line_count() - 1) {
             if (should_wrap)
                 return { 0, 0 };
@@ -619,12 +621,12 @@ VT::Position TerminalWidget::previous_position_before(const VT::Position& positi
         if (position.row() == 0) {
             if (should_wrap) {
                 auto& last_line = m_terminal.line(m_terminal.line_count() - 1);
-                return { static_cast<int>(m_terminal.line_count() - 1), last_line.length() };
+                return { static_cast<int>(m_terminal.line_count() - 1), static_cast<int>(last_line.length()) };
             }
             return {};
         }
         auto& prev_line = m_terminal.line(position.row() - 1);
-        return { position.row() - 1, prev_line.length() };
+        return { position.row() - 1, static_cast<int>(prev_line.length()) };
     }
     return { position.row(), position.column() - 1 };
 }
@@ -909,18 +911,14 @@ 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.attribute_at(column).is_untouched()) {
                 builder.append('\n');
                 break;
             }
             // FIXME: This is a bit hackish.
-            if (line.is_utf32()) {
-                u32 code_point = line.code_point(column);
-                builder.append(Utf32View(&code_point, 1));
-            } else {
-                builder.append(line.code_point(column));
-            }
-            if (column == line.length() - 1 || (m_rectangle_selection && column == last_column)) {
+            u32 code_point = line.code_point(column);
+            builder.append(Utf32View(&code_point, 1));
+            if (column == static_cast<int>(line.length()) - 1 || (m_rectangle_selection && column == last_column)) {
                 builder.append('\n');
             }
         }