Terminal: Add selection and clipboard support
text can now be copied with ctrl+shift+c and pasted with ctrl+shift+v
This commit is contained in:
parent
291f298d19
commit
8f6cb9c057
|
|
@ -6,6 +6,7 @@ set(SOURCES
|
||||||
add_executable(Terminal ${SOURCES})
|
add_executable(Terminal ${SOURCES})
|
||||||
banan_link_library(Terminal ban)
|
banan_link_library(Terminal ban)
|
||||||
banan_link_library(Terminal libc)
|
banan_link_library(Terminal libc)
|
||||||
|
banan_link_library(Terminal libclipboard)
|
||||||
banan_link_library(Terminal libfont)
|
banan_link_library(Terminal libfont)
|
||||||
banan_link_library(Terminal libgui)
|
banan_link_library(Terminal libgui)
|
||||||
banan_link_library(Terminal libinput)
|
banan_link_library(Terminal libinput)
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
#include <BAN/Debug.h>
|
#include <BAN/Debug.h>
|
||||||
#include <BAN/UTF8.h>
|
#include <BAN/UTF8.h>
|
||||||
|
|
||||||
|
#include <LibClipboard/Clipboard.h>
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
@ -127,6 +129,15 @@ void Terminal::run()
|
||||||
|
|
||||||
m_window->set_min_size(m_font.width() * 8, m_font.height() * 2);
|
m_window->set_min_size(m_font.width() * 8, m_font.height() * 2);
|
||||||
|
|
||||||
|
MUST(m_cells.resize(rows() * cols(), {
|
||||||
|
.codepoint = 0,
|
||||||
|
.fg_color = m_fg_color,
|
||||||
|
.bg_color = m_bg_color,
|
||||||
|
.bold = m_is_bold
|
||||||
|
}));
|
||||||
|
m_cells_rows = rows();
|
||||||
|
m_cells_cols = cols();
|
||||||
|
|
||||||
{
|
{
|
||||||
winsize winsize {
|
winsize winsize {
|
||||||
.ws_row = static_cast<unsigned short>(rows()),
|
.ws_row = static_cast<unsigned short>(rows()),
|
||||||
|
|
@ -148,8 +159,31 @@ void Terminal::run()
|
||||||
MUST(m_cursor_buffer.resize(m_font.width() * m_font.height(), m_bg_color));
|
MUST(m_cursor_buffer.resize(m_font.width() * m_font.height(), m_bg_color));
|
||||||
show_cursor();
|
show_cursor();
|
||||||
|
|
||||||
m_window->set_key_event_callback([&](LibGUI::EventPacket::KeyEvent::event_t event) { on_key_event(event); });
|
|
||||||
m_window->set_resize_window_event_callback([&] {
|
m_window->set_resize_window_event_callback([&] {
|
||||||
|
if (cols() != m_cells_cols || rows() != m_cells_rows)
|
||||||
|
{
|
||||||
|
BAN::Vector<Cell> new_cells;
|
||||||
|
MUST(new_cells.resize(rows() * cols(), {
|
||||||
|
.codepoint = 0,
|
||||||
|
.fg_color = m_fg_color,
|
||||||
|
.bg_color = m_bg_color,
|
||||||
|
.bold = m_is_bold,
|
||||||
|
}));
|
||||||
|
|
||||||
|
for (size_t y = 0; y < BAN::Math::min(rows(), m_cells_rows); y++)
|
||||||
|
for (size_t x = 0; x < BAN::Math::min(cols(), m_cells_cols); x++)
|
||||||
|
new_cells[y * cols() + x] = m_cells[y * m_cells_cols + x];
|
||||||
|
m_cells = BAN::move(new_cells);
|
||||||
|
m_cells_rows = rows();
|
||||||
|
m_cells_cols = cols();
|
||||||
|
|
||||||
|
if (m_selection_s_col != UINT32_MAX)
|
||||||
|
{
|
||||||
|
m_selection_s_col = m_selection_s_row = UINT32_MAX;
|
||||||
|
update_selection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (const auto rem = m_window->height() % m_font.height())
|
if (const auto rem = m_window->height() % m_font.height())
|
||||||
{
|
{
|
||||||
m_window->texture().fill_rect(0, m_window->height() - rem, m_window->width(), rem, m_bg_color);
|
m_window->texture().fill_rect(0, m_window->height() - rem, m_window->width(), rem, m_bg_color);
|
||||||
|
|
@ -183,6 +217,113 @@ void Terminal::run()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
m_window->set_key_event_callback([&](LibGUI::EventPacket::KeyEvent::event_t event) {
|
||||||
|
if (event.released())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (event.shift() && event.ctrl())
|
||||||
|
{
|
||||||
|
switch (event.key)
|
||||||
|
{
|
||||||
|
case LibInput::Key::C:
|
||||||
|
{
|
||||||
|
if (m_selection_s_col == UINT32_MAX)
|
||||||
|
break;
|
||||||
|
|
||||||
|
const uint32_t m_selection_start = m_selection_s_row * cols() + m_selection_s_col;
|
||||||
|
const uint32_t m_selection_end = m_selection_e_row * cols() + m_selection_e_col;
|
||||||
|
|
||||||
|
uint32_t selection_min = BAN::Math::min(m_selection_start, m_selection_end);
|
||||||
|
for (; selection_min < m_cells.size(); selection_min++)
|
||||||
|
if (m_cells[selection_min].codepoint != 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
uint32_t selection_max = BAN::Math::max(m_selection_start, m_selection_end);
|
||||||
|
for (; selection_max >= selection_min; selection_max--)
|
||||||
|
if (m_cells[selection_max].codepoint != 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
BAN::String buffer;
|
||||||
|
for (size_t i = selection_min; i <= selection_max; i++)
|
||||||
|
{
|
||||||
|
if (i != selection_min && i % cols() == 0)
|
||||||
|
MUST(buffer.push_back('\n'));
|
||||||
|
|
||||||
|
const auto cell = m_cells[i];
|
||||||
|
if (cell.codepoint == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
char bytes[5];
|
||||||
|
BAN::UTF8::from_codepoints(&cell.codepoint, 1, bytes);
|
||||||
|
MUST(buffer.append(bytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto ret = LibClipboard::Clipboard::set_clipboard_text(buffer); ret.is_error())
|
||||||
|
dwarnln("Failed to set clipboard: {}", ret.error());
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LibInput::Key::V:
|
||||||
|
{
|
||||||
|
auto clipboard_or_error = LibClipboard::Clipboard::get_clipboard_text();
|
||||||
|
if (clipboard_or_error.is_error())
|
||||||
|
{
|
||||||
|
dwarnln("Failed to get clipboard: {}", clipboard_or_error.error());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto clipboard = clipboard_or_error.release_value();
|
||||||
|
if (!clipboard.empty())
|
||||||
|
write(m_shell_info.pts_master, clipboard.data(), clipboard.size());
|
||||||
|
m_got_key_event = true;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const char* text = LibInput::key_to_utf8_ansi(event.key, event.modifier))
|
||||||
|
write(m_shell_info.pts_master, text, strlen(text));
|
||||||
|
m_got_key_event = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
m_window->set_mouse_move_event_callback([&](LibGUI::EventPacket::MouseMoveEvent::event_t event) {
|
||||||
|
if (!m_selecting)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_selection_e_col = BAN::Math::clamp<int32_t>(event.x / static_cast<int32_t>(m_font.width()), 0, cols() - 1);
|
||||||
|
m_selection_e_row = BAN::Math::clamp<int32_t>(event.y / static_cast<int32_t>(m_font.height()), 0, rows() - 1);
|
||||||
|
|
||||||
|
update_selection();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_window->set_mouse_button_event_callback([&](LibGUI::EventPacket::MouseButtonEvent::event_t event) {
|
||||||
|
switch (event.button)
|
||||||
|
{
|
||||||
|
case LibInput::MouseButton::Right:
|
||||||
|
if (!m_selecting)
|
||||||
|
m_selection_s_col = m_selection_s_row = UINT32_MAX;
|
||||||
|
m_selecting = false;
|
||||||
|
break;
|
||||||
|
case LibInput::MouseButton::Left:
|
||||||
|
if (event.pressed)
|
||||||
|
{
|
||||||
|
m_selection_e_col = m_selection_s_col = BAN::Math::clamp<int32_t>(event.x / static_cast<int32_t>(m_font.width()), 0, cols() - 1);
|
||||||
|
m_selection_e_row = m_selection_s_row = BAN::Math::clamp<int32_t>(event.y / static_cast<int32_t>(m_font.height()), 0, rows() - 1);
|
||||||
|
}
|
||||||
|
m_selecting = event.pressed;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
update_selection();
|
||||||
|
});
|
||||||
|
|
||||||
const int max_fd = BAN::Math::max(m_shell_info.pts_master, m_window->server_fd());
|
const int max_fd = BAN::Math::max(m_shell_info.pts_master, m_window->server_fd());
|
||||||
while (!s_shell_exited)
|
while (!s_shell_exited)
|
||||||
{
|
{
|
||||||
|
|
@ -200,7 +341,11 @@ void Terminal::run()
|
||||||
timeval timeout;
|
timeval timeout;
|
||||||
timeout.tv_sec = ms_until_blink / 1'000;
|
timeout.tv_sec = ms_until_blink / 1'000;
|
||||||
timeout.tv_usec = ms_until_blink * 1'000;
|
timeout.tv_usec = ms_until_blink * 1'000;
|
||||||
if (select(max_fd + 1, &fds, nullptr, nullptr, &timeout) == 0)
|
|
||||||
|
const int nselect = select(max_fd + 1, &fds, nullptr, nullptr, &timeout);
|
||||||
|
if (nselect < 0)
|
||||||
|
continue;
|
||||||
|
if (nselect == 0)
|
||||||
{
|
{
|
||||||
m_cursor_blink_shown = !m_cursor_blink_shown;
|
m_cursor_blink_shown = !m_cursor_blink_shown;
|
||||||
m_cursor_blink_ms = current_ms + ms_until_blink;
|
m_cursor_blink_ms = current_ms + ms_until_blink;
|
||||||
|
|
@ -223,6 +368,77 @@ void Terminal::run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Terminal::update_selection(bool show)
|
||||||
|
{
|
||||||
|
static uint32_t old_selection_s_col = UINT32_MAX;
|
||||||
|
static uint32_t old_selection_s_row = UINT32_MAX;
|
||||||
|
static uint32_t old_selection_e_col = 0;
|
||||||
|
static uint32_t old_selection_e_row = 0;
|
||||||
|
|
||||||
|
if (old_selection_s_col != UINT32_MAX)
|
||||||
|
{
|
||||||
|
old_selection_s_col = BAN::Math::min(old_selection_s_col, cols() - 1);
|
||||||
|
old_selection_s_row = BAN::Math::min(old_selection_s_row, rows() - 1);
|
||||||
|
old_selection_e_col = BAN::Math::min(old_selection_e_col, cols() - 1);
|
||||||
|
old_selection_e_row = BAN::Math::min(old_selection_e_row, rows() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle invalidate {};
|
||||||
|
|
||||||
|
const auto redraw_character =
|
||||||
|
[this, &invalidate](size_t index, bool selected)
|
||||||
|
{
|
||||||
|
const uint32_t col = index % cols();
|
||||||
|
const uint32_t row = index / cols();
|
||||||
|
const uint32_t x = col * m_font.width();
|
||||||
|
const uint32_t y = row * m_font.height();
|
||||||
|
const auto cell = m_cells[row * cols() + col];
|
||||||
|
if (selected && cell.codepoint == 0)
|
||||||
|
return;
|
||||||
|
m_window->texture().fill_rect(x, y, m_font.width(), m_font.height(), selected ? s_default_fg_color : cell.bg_color);
|
||||||
|
m_window->texture().draw_character(cell.codepoint, m_font, x, y, selected ? s_default_bg_color : cell.fg_color);
|
||||||
|
invalidate = invalidate.get_bounding_box({ x, y, m_font.width(), m_font.height() });
|
||||||
|
};
|
||||||
|
|
||||||
|
if (old_selection_s_col != UINT32_MAX)
|
||||||
|
{
|
||||||
|
const uint32_t old_selection_start = old_selection_s_row * cols() + old_selection_s_col;
|
||||||
|
const uint32_t old_selection_end = old_selection_e_row * cols() + old_selection_e_col;
|
||||||
|
const auto old_selection_min = BAN::Math::min(old_selection_start, old_selection_end);
|
||||||
|
const auto old_selection_max = BAN::Math::max(old_selection_start, old_selection_end);
|
||||||
|
for (size_t i = old_selection_min; i <= old_selection_max; i++)
|
||||||
|
redraw_character(i, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (show && m_selection_s_col != UINT32_MAX)
|
||||||
|
{
|
||||||
|
const uint32_t new_selection_start = m_selection_s_row * cols() + m_selection_s_col;
|
||||||
|
const uint32_t new_selection_end = m_selection_e_row * cols() + m_selection_e_col;
|
||||||
|
const auto new_selection_min = BAN::Math::min(new_selection_start, new_selection_end);
|
||||||
|
const auto new_selection_max = BAN::Math::max(new_selection_start, new_selection_end);
|
||||||
|
for (size_t i = new_selection_min; i <= new_selection_max; i++)
|
||||||
|
redraw_character(i, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (invalidate.width && invalidate.height)
|
||||||
|
m_window->invalidate(invalidate.x, invalidate.y, invalidate.width, invalidate.height);
|
||||||
|
|
||||||
|
if (show)
|
||||||
|
{
|
||||||
|
old_selection_s_col = m_selection_s_col;
|
||||||
|
old_selection_s_row = m_selection_s_row;
|
||||||
|
old_selection_e_col = m_selection_e_col;
|
||||||
|
old_selection_e_row = m_selection_e_row;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
old_selection_s_col = UINT32_MAX;
|
||||||
|
old_selection_s_row = UINT32_MAX;
|
||||||
|
old_selection_e_col = 0;
|
||||||
|
old_selection_e_row = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Terminal::hide_cursor()
|
void Terminal::hide_cursor()
|
||||||
{
|
{
|
||||||
if (m_cursor.x == cols())
|
if (m_cursor.x == cols())
|
||||||
|
|
@ -297,13 +513,7 @@ bool Terminal::read_shell()
|
||||||
// do possible scrolling already in here, so `putchar()` doesnt
|
// do possible scrolling already in here, so `putchar()` doesnt
|
||||||
// have to scroll up to `rows()` times
|
// have to scroll up to `rows()` times
|
||||||
if (m_cursor.y + newline_count >= rows())
|
if (m_cursor.y + newline_count >= rows())
|
||||||
{
|
should_invalidate = scroll(m_cursor.y + newline_count - rows() + 1);
|
||||||
const uint32_t scroll = m_cursor.y + newline_count - rows() + 1;
|
|
||||||
m_cursor.y -= scroll;
|
|
||||||
m_window->texture().shift_vertical(-scroll * (int32_t)m_font.height());
|
|
||||||
m_window->texture().fill_rect(0, m_window->height() - scroll * m_font.height(), m_window->width(), scroll * m_font.height(), m_bg_color);
|
|
||||||
should_invalidate = { 0, 0, m_window->width(), m_window->height() };
|
|
||||||
}
|
|
||||||
|
|
||||||
i = start;
|
i = start;
|
||||||
for (i = start; i < non_ansi_end; i++)
|
for (i = start; i < non_ansi_end; i++)
|
||||||
|
|
@ -471,6 +681,16 @@ Rectangle Terminal::handle_csi(char ch)
|
||||||
|
|
||||||
if (m_csi_info.fields[0] == -1 || m_csi_info.fields[0] == 0)
|
if (m_csi_info.fields[0] == -1 || m_csi_info.fields[0] == 0)
|
||||||
{
|
{
|
||||||
|
for (size_t i = m_cursor.y * cols() + m_cursor.x; i < m_cells.size(); i++)
|
||||||
|
{
|
||||||
|
m_cells[i] = {
|
||||||
|
.codepoint = 0,
|
||||||
|
.fg_color = m_fg_color,
|
||||||
|
.bg_color = m_bg_color,
|
||||||
|
.bold = m_is_bold,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
rects[0].x = m_cursor.x * m_font.width();
|
rects[0].x = m_cursor.x * m_font.width();
|
||||||
rects[0].y = m_cursor.y * m_font.height();
|
rects[0].y = m_cursor.y * m_font.height();
|
||||||
rects[0].width = m_window->width() - rects[0].x;
|
rects[0].width = m_window->width() - rects[0].x;
|
||||||
|
|
@ -485,6 +705,16 @@ Rectangle Terminal::handle_csi(char ch)
|
||||||
}
|
}
|
||||||
else if (m_csi_info.fields[0] == 1)
|
else if (m_csi_info.fields[0] == 1)
|
||||||
{
|
{
|
||||||
|
for (size_t i = 0; i < m_cursor.y * cols() + m_cursor.x; i++)
|
||||||
|
{
|
||||||
|
m_cells[i] = {
|
||||||
|
.codepoint = 0,
|
||||||
|
.fg_color = m_fg_color,
|
||||||
|
.bg_color = m_bg_color,
|
||||||
|
.bold = m_is_bold,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
rects[0].x = 0;
|
rects[0].x = 0;
|
||||||
rects[0].y = m_cursor.y * m_font.height();
|
rects[0].y = m_cursor.y * m_font.height();
|
||||||
rects[0].width = m_cursor.x * m_font.width();
|
rects[0].width = m_cursor.x * m_font.width();
|
||||||
|
|
@ -499,6 +729,16 @@ Rectangle Terminal::handle_csi(char ch)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
for (size_t i = 0; i < m_cells.size(); i++)
|
||||||
|
{
|
||||||
|
m_cells[i] = {
|
||||||
|
.codepoint = 0,
|
||||||
|
.fg_color = m_fg_color,
|
||||||
|
.bg_color = m_bg_color,
|
||||||
|
.bold = m_is_bold,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
rects[0].x = 0;
|
rects[0].x = 0;
|
||||||
rects[0].y = 0;
|
rects[0].y = 0;
|
||||||
rects[0].width = m_window->width();
|
rects[0].width = m_window->width();
|
||||||
|
|
@ -519,6 +759,16 @@ Rectangle Terminal::handle_csi(char ch)
|
||||||
{
|
{
|
||||||
m_csi_info.fields[0] = BAN::Math::max(m_csi_info.fields[0], 0);
|
m_csi_info.fields[0] = BAN::Math::max(m_csi_info.fields[0], 0);
|
||||||
|
|
||||||
|
const uint32_t s_col = (m_csi_info.fields[0] == 0) ? m_cursor.x : 0;
|
||||||
|
const uint32_t e_col = (m_csi_info.fields[0] == 1) ? m_cursor.x : cols() - 1;
|
||||||
|
for (size_t i = s_col; i <= e_col; i++)
|
||||||
|
m_cells[m_cursor.y * cols() + i] = {
|
||||||
|
.codepoint = ' ',
|
||||||
|
.fg_color = m_fg_color,
|
||||||
|
.bg_color = m_bg_color,
|
||||||
|
.bold = m_is_bold,
|
||||||
|
};
|
||||||
|
|
||||||
Rectangle rect;
|
Rectangle rect;
|
||||||
rect.x = (m_csi_info.fields[0] == 0) ? m_cursor.x * m_font.width() : 0;
|
rect.x = (m_csi_info.fields[0] == 0) ? m_cursor.x * m_font.width() : 0;
|
||||||
rect.y = m_cursor.y * m_font.height();
|
rect.y = m_cursor.y * m_font.height();
|
||||||
|
|
@ -536,6 +786,18 @@ Rectangle Terminal::handle_csi(char ch)
|
||||||
const uint32_t src_y = m_cursor.y * m_font.height();
|
const uint32_t src_y = m_cursor.y * m_font.height();
|
||||||
const uint32_t dst_y = src_y + count * m_font.height();
|
const uint32_t dst_y = src_y + count * m_font.height();
|
||||||
|
|
||||||
|
for (size_t i = m_cursor.y; i + count < rows(); i++)
|
||||||
|
for (size_t col = 0; col < cols(); col++)
|
||||||
|
m_cells[(i + count) * cols() + col] = m_cells[i * cols() + col];
|
||||||
|
for (size_t row = m_cursor.y; row < m_cursor.y + count; row++)
|
||||||
|
for (size_t col = 0; col < cols(); col++)
|
||||||
|
m_cells[row * cols() + col] = {
|
||||||
|
.codepoint = ' ',
|
||||||
|
.fg_color = m_fg_color,
|
||||||
|
.bg_color = m_bg_color,
|
||||||
|
.bold = m_is_bold,
|
||||||
|
};
|
||||||
|
|
||||||
texture.copy_horizontal_slice(dst_y, src_y, m_window->height() - dst_y);
|
texture.copy_horizontal_slice(dst_y, src_y, m_window->height() - dst_y);
|
||||||
texture.fill_rect(0, src_y, m_window->width(), count * m_font.height(), m_bg_color);
|
texture.fill_rect(0, src_y, m_window->width(), count * m_font.height(), m_bg_color);
|
||||||
should_invalidate = {
|
should_invalidate = {
|
||||||
|
|
@ -553,6 +815,18 @@ Rectangle Terminal::handle_csi(char ch)
|
||||||
const uint32_t dst_y = m_cursor.y * m_font.height();
|
const uint32_t dst_y = m_cursor.y * m_font.height();
|
||||||
const uint32_t src_y = dst_y + count * m_font.height();
|
const uint32_t src_y = dst_y + count * m_font.height();
|
||||||
|
|
||||||
|
for (size_t i = m_cursor.y; i + count < rows(); i++)
|
||||||
|
for (size_t col = 0; col < cols(); col++)
|
||||||
|
m_cells[i * cols() + col] = m_cells[(i + count) * cols() + col];
|
||||||
|
for (size_t row = m_cursor.y + count; row < rows(); row++)
|
||||||
|
for (size_t col = 0; col < cols(); col++)
|
||||||
|
m_cells[row * cols() + col] = {
|
||||||
|
.codepoint = ' ',
|
||||||
|
.fg_color = m_fg_color,
|
||||||
|
.bg_color = m_bg_color,
|
||||||
|
.bold = m_is_bold,
|
||||||
|
};
|
||||||
|
|
||||||
texture.copy_horizontal_slice(dst_y, src_y, m_window->height() - dst_y);
|
texture.copy_horizontal_slice(dst_y, src_y, m_window->height() - dst_y);
|
||||||
texture.fill_rect(0, m_window->height() - count * m_font.height(), m_window->width(), count * m_font.height(), m_bg_color);
|
texture.fill_rect(0, m_window->height() - count * m_font.height(), m_window->width(), count * m_font.height(), m_bg_color);
|
||||||
should_invalidate = {
|
should_invalidate = {
|
||||||
|
|
@ -571,6 +845,16 @@ Rectangle Terminal::handle_csi(char ch)
|
||||||
const uint32_t src_x = (m_cursor.x + count) * m_font.width();
|
const uint32_t src_x = (m_cursor.x + count) * m_font.width();
|
||||||
const uint32_t y = m_cursor.y * m_font.height();
|
const uint32_t y = m_cursor.y * m_font.height();
|
||||||
|
|
||||||
|
for (size_t i = m_cursor.x; i + count < cols(); i++)
|
||||||
|
m_cells[m_cursor.y * cols() + i] = m_cells[m_cursor.y * cols() + i + count];
|
||||||
|
for (size_t i = m_cursor.x + count; i < cols(); i++)
|
||||||
|
m_cells[m_cursor.y * cols() + i] = {
|
||||||
|
.codepoint = ' ',
|
||||||
|
.fg_color = m_fg_color,
|
||||||
|
.bg_color = m_bg_color,
|
||||||
|
.bold = m_is_bold,
|
||||||
|
};
|
||||||
|
|
||||||
texture.copy_rect(dst_x, y, src_x, y, m_window->width() - src_x, m_font.height());
|
texture.copy_rect(dst_x, y, src_x, y, m_window->width() - src_x, m_font.height());
|
||||||
texture.fill_rect(m_window->width() - count * m_font.width(), y, count * m_font.width(), m_font.height(), m_bg_color);
|
texture.fill_rect(m_window->width() - count * m_font.width(), y, count * m_font.width(), m_font.height(), m_bg_color);
|
||||||
should_invalidate = {
|
should_invalidate = {
|
||||||
|
|
@ -589,6 +873,16 @@ Rectangle Terminal::handle_csi(char ch)
|
||||||
const uint32_t src_x = m_cursor.x * m_font.width();
|
const uint32_t src_x = m_cursor.x * m_font.width();
|
||||||
const uint32_t y = m_cursor.y * m_font.height();
|
const uint32_t y = m_cursor.y * m_font.height();
|
||||||
|
|
||||||
|
for (size_t i = m_cursor.x; i + count < cols(); i++)
|
||||||
|
m_cells[m_cursor.y * cols() + i + count] = m_cells[m_cursor.y * cols() + i];
|
||||||
|
for (size_t i = m_cursor.x; i < m_cursor.x + count; i++)
|
||||||
|
m_cells[m_cursor.y * cols() + i] = {
|
||||||
|
.codepoint = ' ',
|
||||||
|
.fg_color = m_fg_color,
|
||||||
|
.bg_color = m_bg_color,
|
||||||
|
.bold = m_is_bold,
|
||||||
|
};
|
||||||
|
|
||||||
texture.copy_rect(dst_x, y, src_x, y, m_window->width() - dst_x, m_font.height());
|
texture.copy_rect(dst_x, y, src_x, y, m_window->width() - dst_x, m_font.height());
|
||||||
texture.fill_rect(src_x, y, count * m_font.width(), m_font.height(), m_bg_color);
|
texture.fill_rect(src_x, y, count * m_font.width(), m_font.height(), m_bg_color);
|
||||||
should_invalidate = {
|
should_invalidate = {
|
||||||
|
|
@ -662,6 +956,61 @@ Rectangle Terminal::handle_csi(char ch)
|
||||||
return should_invalidate;
|
return should_invalidate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rectangle Terminal::scroll(uint32_t scroll)
|
||||||
|
{
|
||||||
|
const bool has_selection = (m_selection_s_col != UINT32_MAX);
|
||||||
|
|
||||||
|
if (has_selection)
|
||||||
|
update_selection(false);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < rows() - scroll; i++)
|
||||||
|
for (size_t x = 0; x < cols(); x++)
|
||||||
|
m_cells[i * cols() + x] = m_cells[(i + scroll) * cols() + x];
|
||||||
|
for (size_t i = 0; i < scroll; i++)
|
||||||
|
for (size_t x = 0; x < cols(); x++)
|
||||||
|
m_cells[(rows() - scroll + i) * cols() + x] = {
|
||||||
|
.codepoint = 0,
|
||||||
|
.fg_color = m_fg_color,
|
||||||
|
.bg_color = m_bg_color,
|
||||||
|
.bold = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
auto& texture = m_window->texture();
|
||||||
|
|
||||||
|
m_cursor.y -= scroll;
|
||||||
|
texture.shift_vertical(-scroll * (int32_t)m_font.height());
|
||||||
|
|
||||||
|
const size_t y_off = (rows() - scroll) * m_font.height();
|
||||||
|
texture.fill_rect(0, y_off, m_window->width(), m_window->height() - y_off, m_bg_color);
|
||||||
|
|
||||||
|
if (has_selection)
|
||||||
|
{
|
||||||
|
if (m_selection_s_row < scroll && m_selection_e_row < scroll)
|
||||||
|
m_selection_s_col = m_selection_s_row = UINT32_MAX;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (m_selection_s_row >= scroll)
|
||||||
|
m_selection_s_row -= scroll;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_selection_s_row = 0;
|
||||||
|
m_selection_s_col = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_selection_e_row >= scroll)
|
||||||
|
m_selection_e_row -= scroll;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_selection_e_row = 0;
|
||||||
|
m_selection_e_col = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
update_selection();
|
||||||
|
}
|
||||||
|
|
||||||
|
return { 0, 0, m_window->width(), m_window->height() };
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle Terminal::putcodepoint(uint32_t codepoint)
|
Rectangle Terminal::putcodepoint(uint32_t codepoint)
|
||||||
{
|
{
|
||||||
Rectangle should_invalidate;
|
Rectangle should_invalidate;
|
||||||
|
|
@ -714,15 +1063,7 @@ Rectangle Terminal::putcodepoint(uint32_t codepoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_cursor.y >= rows())
|
if (m_cursor.y >= rows())
|
||||||
{
|
should_invalidate = scroll(m_cursor.y - rows() + 1);
|
||||||
const uint32_t scroll = m_cursor.y - rows() + 1;
|
|
||||||
m_cursor.y -= scroll;
|
|
||||||
texture.shift_vertical(-scroll * (int32_t)m_font.height());
|
|
||||||
|
|
||||||
const size_t y_off = (rows() - scroll) * m_font.height();
|
|
||||||
texture.fill_rect(0, y_off, m_window->width(), m_window->height() - y_off, m_bg_color);
|
|
||||||
should_invalidate = { 0, 0, m_window->width(), m_window->height() };
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint32_t cell_w = m_font.width();
|
const uint32_t cell_w = m_font.width();
|
||||||
const uint32_t cell_h = m_font.height();
|
const uint32_t cell_h = m_font.height();
|
||||||
|
|
@ -732,6 +1073,13 @@ Rectangle Terminal::putcodepoint(uint32_t codepoint)
|
||||||
const auto fg_color = m_colors_inverted ? m_bg_color : m_fg_color;
|
const auto fg_color = m_colors_inverted ? m_bg_color : m_fg_color;
|
||||||
const auto bg_color = m_colors_inverted ? m_fg_color : m_bg_color;
|
const auto bg_color = m_colors_inverted ? m_fg_color : m_bg_color;
|
||||||
|
|
||||||
|
m_cells[m_cursor.y * cols() + m_cursor.x] = {
|
||||||
|
.codepoint = codepoint,
|
||||||
|
.fg_color = fg_color,
|
||||||
|
.bg_color = bg_color,
|
||||||
|
.bold = m_is_bold,
|
||||||
|
};
|
||||||
|
|
||||||
texture.fill_rect(cell_x, cell_y, cell_w, cell_h, bg_color);
|
texture.fill_rect(cell_x, cell_y, cell_w, cell_h, bg_color);
|
||||||
texture.draw_character(codepoint, m_font, cell_x, cell_y, fg_color);
|
texture.draw_character(codepoint, m_font, cell_x, cell_y, fg_color);
|
||||||
if (m_is_bold)
|
if (m_is_bold)
|
||||||
|
|
@ -744,15 +1092,7 @@ Rectangle Terminal::putcodepoint(uint32_t codepoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_cursor.y >= rows())
|
if (m_cursor.y >= rows())
|
||||||
{
|
should_invalidate = scroll(m_cursor.y - rows() + 1);
|
||||||
const uint32_t scroll = m_cursor.y - rows() + 1;
|
|
||||||
m_cursor.y -= scroll;
|
|
||||||
texture.shift_vertical(-scroll * (int32_t)m_font.height());
|
|
||||||
|
|
||||||
const size_t y_off = (rows() - scroll) * m_font.height();
|
|
||||||
texture.fill_rect(0, y_off, m_window->width(), m_window->height() - y_off, m_bg_color);
|
|
||||||
should_invalidate = { 0, 0, m_window->width(), m_window->height() };
|
|
||||||
}
|
|
||||||
|
|
||||||
return should_invalidate;
|
return should_invalidate;
|
||||||
}
|
}
|
||||||
|
|
@ -822,12 +1162,3 @@ Rectangle Terminal::putchar(uint8_t ch)
|
||||||
|
|
||||||
return putcodepoint(codepoint);
|
return putcodepoint(codepoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Terminal::on_key_event(LibGUI::EventPacket::KeyEvent::event_t event)
|
|
||||||
{
|
|
||||||
if (event.released())
|
|
||||||
return;
|
|
||||||
if (const char* text = LibInput::key_to_utf8_ansi(event.key, event.modifier))
|
|
||||||
write(m_shell_info.pts_master, text, strlen(text));
|
|
||||||
m_got_key_event = true;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -36,11 +36,14 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void handle_sgr(int32_t value);
|
void handle_sgr(int32_t value);
|
||||||
|
Rectangle scroll(uint32_t scroll);
|
||||||
Rectangle handle_csi(char ch);
|
Rectangle handle_csi(char ch);
|
||||||
Rectangle putcodepoint(uint32_t codepoint);
|
Rectangle putcodepoint(uint32_t codepoint);
|
||||||
Rectangle putchar(uint8_t ch);
|
Rectangle putchar(uint8_t ch);
|
||||||
bool read_shell();
|
bool read_shell();
|
||||||
|
|
||||||
|
void update_selection(bool show = true);
|
||||||
|
|
||||||
BAN::Optional<uint32_t> get_8bit_color();
|
BAN::Optional<uint32_t> get_8bit_color();
|
||||||
BAN::Optional<uint32_t> get_24bit_color();
|
BAN::Optional<uint32_t> get_24bit_color();
|
||||||
|
|
||||||
|
|
@ -79,6 +82,14 @@ private:
|
||||||
bool question;
|
bool question;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Cell
|
||||||
|
{
|
||||||
|
uint32_t codepoint;
|
||||||
|
uint32_t fg_color;
|
||||||
|
uint32_t bg_color;
|
||||||
|
bool bold;
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BAN::UniqPtr<LibGUI::Window> m_window;
|
BAN::UniqPtr<LibGUI::Window> m_window;
|
||||||
LibFont::Font m_font;
|
LibFont::Font m_font;
|
||||||
|
|
@ -86,6 +97,16 @@ private:
|
||||||
State m_state { State::Normal };
|
State m_state { State::Normal };
|
||||||
CSIInfo m_csi_info;
|
CSIInfo m_csi_info;
|
||||||
|
|
||||||
|
BAN::Vector<Cell> m_cells;
|
||||||
|
uint32_t m_cells_rows { 0 };
|
||||||
|
uint32_t m_cells_cols { 0 };
|
||||||
|
|
||||||
|
uint32_t m_selection_s_col { UINT32_MAX };
|
||||||
|
uint32_t m_selection_s_row { UINT32_MAX };
|
||||||
|
uint32_t m_selection_e_col { UINT32_MAX };
|
||||||
|
uint32_t m_selection_e_row { UINT32_MAX };
|
||||||
|
bool m_selecting { false };
|
||||||
|
|
||||||
bool m_cursor_shown { true };
|
bool m_cursor_shown { true };
|
||||||
bool m_cursor_blink_shown { true };
|
bool m_cursor_blink_shown { true };
|
||||||
uint64_t m_cursor_blink_ms { 0 };
|
uint64_t m_cursor_blink_ms { 0 };
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue