|
@@ -7,6 +7,7 @@
|
|
|
|
|
|
#include "Terminal.h"
|
|
|
#include <AK/Debug.h>
|
|
|
+#include <AK/Queue.h>
|
|
|
#include <AK/StringBuilder.h>
|
|
|
#include <AK/StringView.h>
|
|
|
#include <AK/TemporaryChange.h>
|
|
@@ -1439,6 +1440,95 @@ void Terminal::set_size(u16 columns, u16 rows)
|
|
|
if (columns == m_columns && rows == m_rows)
|
|
|
return;
|
|
|
|
|
|
+ // If we're making the terminal larger (column-wise), start at the end and go up, taking cells from the line below.
|
|
|
+ // otherwise start at the beginning and go down, pushing cells into the line below.
|
|
|
+ auto resize_and_rewrap = [&](auto& buffer, auto& old_cursor) {
|
|
|
+ auto cursor_on_line = [&](auto index) {
|
|
|
+ return index == old_cursor.row ? &old_cursor : nullptr;
|
|
|
+ };
|
|
|
+ // Two passes, one from top to bottom, another from bottom to top
|
|
|
+ for (size_t pass = 0; pass < 2; ++pass) {
|
|
|
+ auto forwards = (pass == 0) ^ (columns < m_columns);
|
|
|
+ if (forwards) {
|
|
|
+ for (size_t i = 1; i <= buffer.size(); ++i) {
|
|
|
+ auto is_at_seam = i == 1;
|
|
|
+ auto next_line = is_at_seam ? nullptr : &buffer[buffer.size() - i + 1];
|
|
|
+ auto& line = buffer[buffer.size() - i];
|
|
|
+ auto next_cursor = cursor_on_line(buffer.size() - i + 1);
|
|
|
+ line.set_length(columns, next_line, next_cursor ?: cursor_on_line(buffer.size() - i), !!next_cursor);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ for (size_t i = 0; i < buffer.size(); ++i) {
|
|
|
+ auto is_at_seam = i + 1 == buffer.size();
|
|
|
+ auto next_line = is_at_seam ? nullptr : &buffer[i + 1];
|
|
|
+ auto next_cursor = cursor_on_line(i + 1);
|
|
|
+ buffer[i].set_length(columns, next_line, next_cursor ?: cursor_on_line(i), !!next_cursor);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Queue<size_t> lines_to_reevaluate;
|
|
|
+ for (size_t i = 0; i < buffer.size(); ++i) {
|
|
|
+ if (buffer[i].length() != columns)
|
|
|
+ lines_to_reevaluate.enqueue(i);
|
|
|
+ }
|
|
|
+ size_t rows_inserted = 0;
|
|
|
+ while (!lines_to_reevaluate.is_empty()) {
|
|
|
+ auto index = lines_to_reevaluate.dequeue();
|
|
|
+ auto is_at_seam = index + 1 == buffer.size();
|
|
|
+ auto next_line = is_at_seam ? nullptr : &buffer[index + 1];
|
|
|
+ auto& line = buffer[index];
|
|
|
+ auto next_cursor = cursor_on_line(index + 1);
|
|
|
+ line.set_length(columns, next_line, next_cursor ?: cursor_on_line(index), !!next_cursor);
|
|
|
+ if (line.length() > columns) {
|
|
|
+ auto current_cursor = cursor_on_line(index);
|
|
|
+ // Split the line into two (or more)
|
|
|
+ ++index;
|
|
|
+ ++rows_inserted;
|
|
|
+ buffer.insert(index, make<Line>(0));
|
|
|
+ VERIFY(buffer[index].length() == 0);
|
|
|
+ line.set_length(columns, &buffer[index], current_cursor, false);
|
|
|
+ // If we inserted a line and the old cursor was after that line, increment its row
|
|
|
+ if (!current_cursor && old_cursor.row >= index)
|
|
|
+ ++old_cursor.row;
|
|
|
+
|
|
|
+ if (buffer[index].length() != columns)
|
|
|
+ lines_to_reevaluate.enqueue(index);
|
|
|
+ }
|
|
|
+ if (next_line && next_line->length() != columns)
|
|
|
+ lines_to_reevaluate.enqueue(index + 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return old_cursor;
|
|
|
+ };
|
|
|
+
|
|
|
+ CursorPosition cursor_tracker { cursor_row(), cursor_column() };
|
|
|
+ resize_and_rewrap(m_normal_screen_buffer, cursor_tracker);
|
|
|
+ if (m_normal_screen_buffer.size() > rows) {
|
|
|
+ if (auto extra_lines = m_normal_screen_buffer.size() - rows) {
|
|
|
+ while (extra_lines > 0) {
|
|
|
+ if (m_normal_screen_buffer.size() <= cursor_tracker.row)
|
|
|
+ break;
|
|
|
+ if (m_normal_screen_buffer.last().is_empty()) {
|
|
|
+ if (m_normal_screen_buffer[m_normal_screen_buffer.size() - 2].termination_column().has_value())
|
|
|
+ break;
|
|
|
+ --extra_lines;
|
|
|
+ m_normal_screen_buffer.take_last();
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ for (size_t i = 0; i < extra_lines; ++i)
|
|
|
+ m_history.append(m_normal_screen_buffer.take_first());
|
|
|
+ m_client.terminal_history_changed(extra_lines);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ CursorPosition dummy_cursor_tracker {};
|
|
|
+ resize_and_rewrap(m_alternate_screen_buffer, dummy_cursor_tracker);
|
|
|
+ if (m_alternate_screen_buffer.size() > rows)
|
|
|
+ m_alternate_screen_buffer.remove(0, m_alternate_screen_buffer.size() - rows);
|
|
|
+
|
|
|
if (rows > m_rows) {
|
|
|
while (m_normal_screen_buffer.size() < rows)
|
|
|
m_normal_screen_buffer.append(make<Line>(columns));
|
|
@@ -1449,11 +1539,6 @@ void Terminal::set_size(u16 columns, u16 rows)
|
|
|
m_alternate_screen_buffer.shrink(rows);
|
|
|
}
|
|
|
|
|
|
- for (int i = 0; i < rows; ++i) {
|
|
|
- m_normal_screen_buffer[i].set_length(columns);
|
|
|
- m_alternate_screen_buffer[i].set_length(columns);
|
|
|
- }
|
|
|
-
|
|
|
m_columns = columns;
|
|
|
m_rows = rows;
|
|
|
|
|
@@ -1471,6 +1556,8 @@ void Terminal::set_size(u16 columns, u16 rows)
|
|
|
// Rightmost column is always last tab on line.
|
|
|
m_horizontal_tabs[columns - 1] = 1;
|
|
|
|
|
|
+ set_cursor(cursor_tracker.row, cursor_tracker.column);
|
|
|
+
|
|
|
m_client.terminal_did_resize(m_columns, m_rows);
|
|
|
|
|
|
dbgln_if(TERMINAL_DEBUG, "Set terminal size: {}x{}", m_rows, m_columns);
|
|
@@ -1480,7 +1567,8 @@ void Terminal::set_size(u16 columns, u16 rows)
|
|
|
#ifndef KERNEL
|
|
|
void Terminal::invalidate_cursor()
|
|
|
{
|
|
|
- active_buffer()[cursor_row()].set_dirty(true);
|
|
|
+ if (cursor_row() < active_buffer().size())
|
|
|
+ active_buffer()[cursor_row()].set_dirty(true);
|
|
|
}
|
|
|
|
|
|
Attribute Terminal::attribute_at(const Position& position) const
|