ソースを参照

Kernel+LibVT: Add function for deleting a range of characters

Previously, this was done by telling the client to put a space at each
character in the range. This was inefficient, because a large number of
function calls took place and incorrect, as the ANSI standard dictates
that character attributes should be cleared as well.

The newly added `clear_in_line` function solves this issue. It performs
just one bounds check when it's called and can be implemented as a
pretty tight loop.
Daniel Bertalan 4 年 前
コミット
7419569a2b

+ 12 - 6
Kernel/TTY/VirtualConsole.cpp

@@ -81,6 +81,11 @@ void ConsoleImpl::put_character_at(unsigned row, unsigned column, u32 ch)
     m_last_code_point = ch;
 }
 
+void ConsoleImpl::clear_in_line(u16 row, u16 first_column, u16 last_column)
+{
+    m_client.clear_in_line(row, first_column, last_column);
+}
+
 void ConsoleImpl::ICH(Parameters)
 {
     // FIXME: Implement this
@@ -420,13 +425,14 @@ void VirtualConsole::scroll_down(u16 region_top, u16 region_bottom, size_t count
         m_lines[row].dirty = true;
 }
 
-void VirtualConsole::clear_line(size_t y_index)
+void VirtualConsole::clear_in_line(u16 row, u16 first_column, u16 last_column)
 {
-    m_lines[y_index].dirty = true;
-    for (size_t x = 0; x < columns(); x++) {
-        auto& cell = cell_at(x, y_index);
-        cell.clear();
-    }
+    VERIFY(row < rows());
+    VERIFY(first_column <= last_column);
+    VERIFY(last_column < columns());
+    m_lines[row].dirty = true;
+    for (size_t x = first_column; x <= last_column; x++)
+        cell_at(x, row).clear();
 }
 
 void VirtualConsole::put_character_at(unsigned row, unsigned column, u32 code_point, const VT::Attribute& attribute)

+ 6 - 1
Kernel/TTY/VirtualConsole.h

@@ -40,6 +40,7 @@ private:
     virtual void scroll_up(u16 region_top, u16 region_bottom, size_t count) override;
     virtual void scroll_down(u16 region_top, u16 region_bottom, size_t count) override;
     virtual void put_character_at(unsigned row, unsigned column, u32 ch) override;
+    virtual void clear_in_line(u16 row, u16 first_column, u16 last_column) override;
 
     virtual void ICH(Parameters) override;
     virtual void DCH(Parameters) override;
@@ -137,7 +138,11 @@ private:
 
     void scroll_down(u16 region_top, u16 region_bottom, size_t count);
     void scroll_up(u16 region_top, u16 region_bottom, size_t count);
-    void clear_line(size_t index);
+    void clear_line(size_t index)
+    {
+        clear_in_line(index, 0, m_console_impl.columns() - 1);
+    }
+    void clear_in_line(u16 row, u16 first_column, u16 last_column);
     void put_character_at(unsigned row, unsigned column, u32 ch, const VT::Attribute&);
 
     OwnPtr<Region> m_cells;

+ 11 - 8
Userland/Libraries/LibVT/Attribute.h

@@ -15,8 +15,6 @@
 namespace VT {
 
 struct Attribute {
-    Attribute() { reset(); }
-
     static constexpr Color default_foreground_color = Color::named(Color::ANSIColor::DefaultForeground);
     static constexpr Color default_background_color = Color::named(Color::ANSIColor::DefaultBackground);
 
@@ -25,12 +23,17 @@ struct Attribute {
         foreground_color = default_foreground_color;
         background_color = default_background_color;
         flags = Flags::NoAttributes;
+#ifndef KERNEL
+        href = {};
+        href_id = {};
+#endif
     }
+
     Color foreground_color { default_foreground_color };
     Color background_color { default_background_color };
 
-    Color effective_background_color() const { return flags & Negative ? foreground_color : background_color; }
-    Color effective_foreground_color() const { return flags & Negative ? background_color : foreground_color; }
+    constexpr Color effective_background_color() const { return flags & Negative ? foreground_color : background_color; }
+    constexpr Color effective_foreground_color() const { return flags & Negative ? background_color : foreground_color; }
 
 #ifndef KERNEL
     String href;
@@ -47,17 +50,17 @@ struct Attribute {
         Touched = 0x20,
     };
 
-    bool is_untouched() const { return !(flags & Touched); }
+    constexpr 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;
+    u8 flags { Flags::NoAttributes };
 
-    bool operator==(const Attribute& other) const
+    constexpr 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
+    constexpr bool operator!=(const Attribute& other) const
     {
         return !(*this == other);
     }

+ 5 - 8
Userland/Libraries/LibVT/Line.cpp

@@ -25,15 +25,12 @@ void Line::set_length(size_t new_length)
     m_cells.resize(new_length);
 }
 
-void Line::clear(const Attribute& attribute)
+void Line::clear_range(size_t first_column, size_t last_column, const Attribute& attribute)
 {
-    if (m_dirty) {
-        for (auto& cell : m_cells) {
-            cell = Cell { .code_point = ' ', .attribute = attribute };
-        }
-        return;
-    }
-    for (auto& cell : m_cells) {
+    VERIFY(first_column <= last_column);
+    VERIFY(last_column < m_cells.size());
+    for (size_t i = first_column; i <= last_column; ++i) {
+        auto& cell = m_cells[i];
         if (!m_dirty)
             m_dirty = cell.code_point != ' ' || cell.attribute != attribute;
         cell = Cell { .code_point = ' ', .attribute = attribute };

+ 5 - 1
Userland/Libraries/LibVT/Line.h

@@ -33,7 +33,11 @@ public:
     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&);
+    void clear(const Attribute& attribute = Attribute())
+    {
+        clear_range(0, m_cells.size() - 1, attribute);
+    }
+    void clear_range(size_t first_column, size_t last_column, const Attribute& attribute = Attribute());
     bool has_only_one_background_color() const;
 
     size_t length() const { return m_cells.size(); }

+ 19 - 29
Userland/Libraries/LibVT/Terminal.cpp

@@ -32,7 +32,7 @@ void Terminal::clear()
 {
     dbgln_if(TERMINAL_DEBUG, "Clear the entire screen");
     for (size_t i = 0; i < rows(); ++i)
-        active_buffer()[i].clear(Attribute());
+        active_buffer()[i].clear();
     set_cursor(0, 0);
 }
 
@@ -559,9 +559,7 @@ void Terminal::ECH(Parameters params)
     // Clear num characters from the right of the cursor.
     auto clear_end = min<unsigned>(m_columns, cursor_column() + num - 1);
     dbgln_if(TERMINAL_DEBUG, "Erase characters {}-{} on line {}", cursor_column(), clear_end, cursor_row());
-    for (unsigned i = cursor_column(); i <= clear_end; ++i) {
-        put_character_at(cursor_row(), i, ' ');
-    }
+    clear_in_line(cursor_row(), cursor_column(), clear_end);
 }
 
 void Terminal::EL(Parameters params)
@@ -572,21 +570,15 @@ void Terminal::EL(Parameters params)
     switch (mode) {
     case 0:
         dbgln_if(TERMINAL_DEBUG, "Clear line {} from cursor column ({}) to the end", cursor_row(), cursor_column());
-        for (int i = cursor_column(); i < m_columns; ++i) {
-            put_character_at(cursor_row(), i, ' ');
-        }
+        clear_in_line(cursor_row(), cursor_column(), m_columns - 1);
         break;
     case 1:
         dbgln_if(TERMINAL_DEBUG, "Clear line {} from the start to cursor column ({})", cursor_row(), cursor_column());
-        for (int i = 0; i <= cursor_column(); ++i) {
-            put_character_at(cursor_row(), i, ' ');
-        }
+        clear_in_line(cursor_row(), 0, cursor_column());
         break;
     case 2:
         dbgln_if(TERMINAL_DEBUG, "Clear line {} completely", cursor_row());
-        for (int i = 0; i < m_columns; ++i) {
-            put_character_at(cursor_row(), i, ' ');
-        }
+        clear_in_line(cursor_row(), 0, m_columns - 1);
         break;
     default:
         unimplemented_csi_sequence(params, {}, 'K');
@@ -602,23 +594,15 @@ void Terminal::ED(Parameters params)
     switch (mode) {
     case 0:
         dbgln_if(TERMINAL_DEBUG, "Clear from cursor ({},{}) to end of screen", cursor_row(), cursor_column());
-        for (int i = cursor_column(); i < m_columns; ++i)
-            put_character_at(cursor_row(), i, ' ');
-        for (int row = cursor_row() + 1; row < m_rows; ++row) {
-            for (int column = 0; column < m_columns; ++column) {
-                put_character_at(row, column, ' ');
-            }
-        }
+        clear_in_line(cursor_row(), cursor_column(), m_columns - 1);
+        for (int row = cursor_row() + 1; row < m_rows; ++row)
+            clear_in_line(row, 0, m_columns - 1);
         break;
     case 1:
         dbgln_if(TERMINAL_DEBUG, "Clear from beginning of screen to cursor ({},{})", cursor_row(), cursor_column());
-        for (int i = cursor_column(); i >= 0; --i)
-            put_character_at(cursor_row(), i, ' ');
-        for (int row = cursor_row() - 1; row >= 0; --row) {
-            for (int column = 0; column < m_columns; ++column) {
-                put_character_at(row, column, ' ');
-            }
-        }
+        clear_in_line(cursor_row(), 0, cursor_column());
+        for (int row = cursor_row() - 1; row >= 0; --row)
+            clear_in_line(row, 0, m_columns - 1);
         break;
     case 2:
         clear();
@@ -789,7 +773,7 @@ void Terminal::scroll_up(u16 region_top, u16 region_bottom, size_t count)
     } else {
         // The new lines haven't been moved and we don't want to leak memory.
         for (u16 row = region_bottom + 1 - count; row <= region_bottom; ++row)
-            active_buffer()[row].clear(Attribute());
+            active_buffer()[row].clear();
     }
     // Set dirty flag on swapped lines.
     // The other lines have implicitly been set dirty by being cleared.
@@ -816,7 +800,7 @@ void Terminal::scroll_down(u16 region_top, u16 region_bottom, size_t count)
         swap(active_buffer().ptr_at(row), active_buffer().ptr_at(row - count));
     // Clear the 'new' lines at the top.
     for (u16 row = region_top; row < region_top + count; ++row)
-        active_buffer()[row].clear(Attribute());
+        active_buffer()[row].clear();
     // Set dirty flag on swapped lines.
     // The other lines have implicitly been set dirty by being cleared.
     for (u16 row = region_top + count; row <= region_bottom; ++row)
@@ -835,6 +819,12 @@ void Terminal::put_character_at(unsigned row, unsigned column, u32 code_point)
 
     m_last_code_point = code_point;
 }
+
+void Terminal::clear_in_line(u16 row, u16 first_column, u16 last_column)
+{
+    VERIFY(row < rows());
+    active_buffer()[row].clear_range(first_column, last_column);
+}
 #endif
 
 void Terminal::set_cursor(unsigned a_row, unsigned a_column, bool skip_debug)

+ 2 - 0
Userland/Libraries/LibVT/Terminal.h

@@ -217,10 +217,12 @@ protected:
     void scroll_up(u16 region_top, u16 region_bottom, size_t count);
     void scroll_down(u16 region_top, u16 region_bottom, size_t count);
     void put_character_at(unsigned row, unsigned column, u32 ch);
+    void clear_in_line(u16 row, u16 first_column, u16 last_column);
 #else
     virtual void scroll_up(u16 region_top, u16 region_bottom, size_t count) = 0;
     virtual void scroll_down(u16 region_top, u16 region_bottom, size_t count) = 0;
     virtual void put_character_at(unsigned row, unsigned column, u32 ch) = 0;
+    virtual void clear_in_line(u16 row, u16 first_column, u16 last_column) = 0;
 #endif
 
     void unimplemented_control_code(u8);