mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 16:47:36 +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:
parent
8f8fd9c5a8
commit
7419569a2b
7 changed files with 60 additions and 53 deletions
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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(); }
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue