1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 19:47:44 +00:00

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.
This commit is contained in:
Daniel Bertalan 2021-06-05 11:12:00 +02:00 committed by Andreas Kling
parent 8f8fd9c5a8
commit 7419569a2b
7 changed files with 60 additions and 53 deletions

View file

@ -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)

View file

@ -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;

View file

@ -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);
}

View file

@ -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 };

View file

@ -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(); }

View file

@ -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)

View file

@ -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);