mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 15:40:19 +00:00
LibVT+Kernel: Add support for setting cursor styles
This commit introduces support for 3 new escape sequences: 1. Stop blinking cursor mode 2. `DECTCEM` mode (enable/disable cursor) 3. `DECSCUSR` (set cursor style) `TerminalWidget` now supports the following cursor types: block, underline and vertical bar. Each of these can blink or be steady. `VirtualConsole` ignores these (just as we were doing before).
This commit is contained in:
parent
7dfc804d7d
commit
875a2cbb71
Notes:
sideshowbarker
2024-07-18 17:28:13 +09:00
Author: https://github.com/BertalanD Commit: https://github.com/SerenityOS/serenity/commit/875a2cbb715 Pull-request: https://github.com/SerenityOS/serenity/pull/7438
6 changed files with 162 additions and 14 deletions
|
@ -387,6 +387,11 @@ void VirtualConsole::emit(const u8* data, size_t size)
|
|||
TTY::emit(data[i], true);
|
||||
}
|
||||
|
||||
void VirtualConsole::set_cursor_style(VT::CursorStyle)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
String VirtualConsole::device_name() const
|
||||
{
|
||||
return String::formatted("tty{}", minor());
|
||||
|
|
|
@ -106,6 +106,7 @@ private:
|
|||
virtual void terminal_did_resize(u16 columns, u16 rows) override;
|
||||
virtual void terminal_history_changed() override;
|
||||
virtual void emit(const u8*, size_t) override;
|
||||
virtual void set_cursor_style(VT::CursorStyle) override;
|
||||
|
||||
// ^CharacterDevice
|
||||
virtual const char* class_name() const override { return "VirtualConsole"; }
|
||||
|
|
|
@ -46,24 +46,64 @@ void Terminal::clear_including_history()
|
|||
|
||||
void Terminal::alter_mode(bool should_set, Parameters params, Intermediates intermediates)
|
||||
{
|
||||
auto steady_cursor_to_blinking = [](CursorStyle style) {
|
||||
switch (style) {
|
||||
case SteadyBar:
|
||||
return BlinkingBar;
|
||||
case SteadyBlock:
|
||||
return BlinkingBlock;
|
||||
case SteadyUnderline:
|
||||
return BlinkingUnderline;
|
||||
default:
|
||||
return style;
|
||||
}
|
||||
};
|
||||
|
||||
auto blinking_cursor_to_steady = [](CursorStyle style) {
|
||||
switch (style) {
|
||||
case BlinkingBar:
|
||||
return SteadyBar;
|
||||
case BlinkingBlock:
|
||||
return SteadyBlock;
|
||||
case BlinkingUnderline:
|
||||
return SteadyUnderline;
|
||||
default:
|
||||
return style;
|
||||
}
|
||||
};
|
||||
|
||||
if (intermediates.size() > 0 && intermediates[0] == '?') {
|
||||
for (auto mode : params) {
|
||||
switch (mode) {
|
||||
case 3: {
|
||||
// 80/132-column mode (DECCOLM)
|
||||
unsigned new_columns = should_set ? 80 : 132;
|
||||
unsigned new_columns = should_set ? 132 : 80;
|
||||
dbgln_if(TERMINAL_DEBUG, "Setting {}-column mode", new_columns);
|
||||
set_size(new_columns, rows());
|
||||
clear();
|
||||
break;
|
||||
}
|
||||
case 12:
|
||||
if (should_set) {
|
||||
// Start blinking cursor
|
||||
m_cursor_style = steady_cursor_to_blinking(m_cursor_style);
|
||||
} else {
|
||||
// Stop blinking cursor
|
||||
m_cursor_style = blinking_cursor_to_steady(m_cursor_style);
|
||||
}
|
||||
m_client.set_cursor_style(m_cursor_style);
|
||||
break;
|
||||
case 25:
|
||||
// Hide cursor command, but doesn't need to be run (for now, because
|
||||
// we don't do inverse control codes anyways)
|
||||
if (should_set)
|
||||
dbgln("Terminal: Hide Cursor escapecode received. Not needed: ignored.");
|
||||
else
|
||||
dbgln("Terminal: Show Cursor escapecode received. Not needed: ignored.");
|
||||
if (should_set) {
|
||||
// Show cursor
|
||||
m_cursor_style = m_saved_cursor_style;
|
||||
m_client.set_cursor_style(m_cursor_style);
|
||||
} else {
|
||||
// Hide cursor
|
||||
m_saved_cursor_style = m_cursor_style;
|
||||
m_cursor_style = None;
|
||||
m_client.set_cursor_style(None);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
dbgln("Terminal::alter_mode: Unimplemented private mode {} (should_set={})", mode, should_set);
|
||||
|
@ -84,12 +124,12 @@ void Terminal::alter_mode(bool should_set, Parameters params, Intermediates inte
|
|||
|
||||
void Terminal::RM(Parameters params, Intermediates intermediates)
|
||||
{
|
||||
alter_mode(true, params, intermediates);
|
||||
alter_mode(false, params, intermediates);
|
||||
}
|
||||
|
||||
void Terminal::SM(Parameters params, Intermediates intermediates)
|
||||
{
|
||||
alter_mode(false, params, intermediates);
|
||||
alter_mode(true, params, intermediates);
|
||||
}
|
||||
|
||||
void Terminal::SGR(Parameters params)
|
||||
|
@ -454,6 +494,35 @@ void Terminal::SD(Parameters params)
|
|||
scroll_down();
|
||||
}
|
||||
|
||||
void Terminal::DECSCUSR(Parameters params)
|
||||
{
|
||||
unsigned style = 1;
|
||||
if (params.size() >= 1 && params[0] != 0)
|
||||
style = params[0];
|
||||
switch (style) {
|
||||
case 1:
|
||||
m_client.set_cursor_style(BlinkingBlock);
|
||||
break;
|
||||
case 2:
|
||||
m_client.set_cursor_style(SteadyBlock);
|
||||
break;
|
||||
case 3:
|
||||
m_client.set_cursor_style(BlinkingUnderline);
|
||||
break;
|
||||
case 4:
|
||||
m_client.set_cursor_style(SteadyUnderline);
|
||||
break;
|
||||
case 5:
|
||||
m_client.set_cursor_style(BlinkingBar);
|
||||
break;
|
||||
case 6:
|
||||
m_client.set_cursor_style(SteadyBar);
|
||||
break;
|
||||
default:
|
||||
dbgln("Unknown cursor style {}", style);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef KERNEL
|
||||
void Terminal::IL(Parameters params)
|
||||
{
|
||||
|
@ -833,6 +902,12 @@ void Terminal::execute_csi_sequence(Parameters parameters, Intermediates interme
|
|||
case 'n':
|
||||
DSR(parameters);
|
||||
break;
|
||||
case 'q':
|
||||
if (intermediates.size() >= 1 && intermediates[0] == ' ')
|
||||
DECSCUSR(parameters);
|
||||
else
|
||||
unimplemented_csi_sequence(parameters, intermediates, last_byte);
|
||||
break;
|
||||
default:
|
||||
unimplemented_csi_sequence(parameters, intermediates, last_byte);
|
||||
}
|
||||
|
|
|
@ -26,6 +26,16 @@ class VirtualConsole;
|
|||
|
||||
namespace VT {
|
||||
|
||||
enum CursorStyle {
|
||||
None,
|
||||
BlinkingBlock,
|
||||
SteadyBlock,
|
||||
BlinkingUnderline,
|
||||
SteadyUnderline,
|
||||
BlinkingBar,
|
||||
SteadyBar
|
||||
};
|
||||
|
||||
class TerminalClient {
|
||||
public:
|
||||
virtual ~TerminalClient() { }
|
||||
|
@ -36,6 +46,7 @@ public:
|
|||
virtual void terminal_did_resize(u16 columns, u16 rows) = 0;
|
||||
virtual void terminal_history_changed() = 0;
|
||||
virtual void emit(const u8*, size_t) = 0;
|
||||
virtual void set_cursor_style(CursorStyle) = 0;
|
||||
};
|
||||
|
||||
class Terminal : public EscapeSequenceExecutor {
|
||||
|
@ -238,6 +249,9 @@ protected:
|
|||
// DSR - Device Status Reports
|
||||
void DSR(Parameters);
|
||||
|
||||
// DECSCUSR - Set Cursor Style
|
||||
void DECSCUSR(Parameters);
|
||||
|
||||
#ifndef KERNEL
|
||||
// ICH - Insert Character
|
||||
void ICH(Parameters);
|
||||
|
@ -319,6 +333,9 @@ protected:
|
|||
bool m_swallow_current { false };
|
||||
bool m_stomp { false };
|
||||
|
||||
CursorStyle m_cursor_style { BlinkingBlock };
|
||||
CursorStyle m_saved_cursor_style { BlinkingBlock };
|
||||
|
||||
Attribute m_current_attribute;
|
||||
Attribute m_saved_attribute;
|
||||
|
||||
|
|
|
@ -326,6 +326,7 @@ void TerminalWidget::paint_event(GUI::PaintEvent& event)
|
|||
|
||||
for (size_t column = 0; column < line.length(); ++column) {
|
||||
bool should_reverse_fill_for_cursor_or_selection = m_cursor_blink_state
|
||||
&& (m_cursor_style == VT::CursorStyle::SteadyBlock || m_cursor_style == VT::CursorStyle::BlinkingBlock)
|
||||
&& m_has_logical_focus
|
||||
&& visual_row == row_with_cursor
|
||||
&& column == m_terminal.cursor_column();
|
||||
|
@ -392,6 +393,7 @@ void TerminalWidget::paint_event(GUI::PaintEvent& event)
|
|||
for (size_t column = 0; column < line.length(); ++column) {
|
||||
auto attribute = line.attribute_at(column);
|
||||
bool should_reverse_fill_for_cursor_or_selection = m_cursor_blink_state
|
||||
&& (m_cursor_style == VT::CursorStyle::SteadyBlock || m_cursor_style == VT::CursorStyle::BlinkingBlock)
|
||||
&& m_has_logical_focus
|
||||
&& visual_row == row_with_cursor
|
||||
&& column == m_terminal.cursor_column();
|
||||
|
@ -417,11 +419,31 @@ void TerminalWidget::paint_event(GUI::PaintEvent& event)
|
|||
}
|
||||
|
||||
// Draw cursor.
|
||||
if (!m_has_logical_focus && row_with_cursor < m_terminal.rows()) {
|
||||
if (m_cursor_blink_state && row_with_cursor < m_terminal.rows()) {
|
||||
auto& cursor_line = m_terminal.line(first_row_from_history + row_with_cursor);
|
||||
if (m_terminal.cursor_row() < (m_terminal.rows() - rows_from_history)) {
|
||||
auto cell_rect = glyph_rect(row_with_cursor, m_terminal.cursor_column()).inflated(0, m_line_spacing);
|
||||
painter.draw_rect(cell_rect, color_from_rgb(cursor_line.attribute_at(m_terminal.cursor_column()).effective_foreground_color()));
|
||||
if (m_terminal.cursor_row() >= (m_terminal.rows() - rows_from_history))
|
||||
return;
|
||||
|
||||
if (m_has_logical_focus && (m_cursor_style == VT::CursorStyle::BlinkingBlock || m_cursor_style == VT::CursorStyle::SteadyBlock))
|
||||
return; // This has already been handled by inverting the cell colors
|
||||
|
||||
auto cursor_color = color_from_rgb(cursor_line.attribute_at(m_terminal.cursor_column()).effective_foreground_color());
|
||||
auto cell_rect = glyph_rect(row_with_cursor, m_terminal.cursor_column()).inflated(0, m_line_spacing);
|
||||
if (m_cursor_style == VT::CursorStyle::BlinkingUnderline || m_cursor_style == VT::CursorStyle::SteadyUnderline) {
|
||||
auto x1 = cell_rect.bottom_left().x();
|
||||
auto x2 = cell_rect.bottom_right().x();
|
||||
auto y = cell_rect.bottom_left().y();
|
||||
for (auto x = x1; x <= x2; ++x)
|
||||
painter.set_pixel({ x, y }, cursor_color);
|
||||
} else if (m_cursor_style == VT::CursorStyle::BlinkingBar || m_cursor_style == VT::CursorStyle::SteadyBar) {
|
||||
auto x = cell_rect.bottom_left().x();
|
||||
auto y1 = cell_rect.top_left().y();
|
||||
auto y2 = cell_rect.bottom_left().y();
|
||||
for (auto y = y1; y <= y2; ++y)
|
||||
painter.set_pixel({ x, y }, cursor_color);
|
||||
} else {
|
||||
// We fall back to a block if we don't support the selected cursor type.
|
||||
painter.draw_rect(cell_rect, cursor_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -986,6 +1008,32 @@ void TerminalWidget::emit(const u8* data, size_t size)
|
|||
}
|
||||
}
|
||||
|
||||
void TerminalWidget::set_cursor_style(CursorStyle style)
|
||||
{
|
||||
switch (style) {
|
||||
case None:
|
||||
m_cursor_blink_timer->stop();
|
||||
m_cursor_blink_state = false;
|
||||
break;
|
||||
case SteadyBlock:
|
||||
case SteadyUnderline:
|
||||
case SteadyBar:
|
||||
m_cursor_blink_timer->stop();
|
||||
m_cursor_blink_state = true;
|
||||
break;
|
||||
case BlinkingBlock:
|
||||
case BlinkingUnderline:
|
||||
case BlinkingBar:
|
||||
m_cursor_blink_state = true;
|
||||
m_cursor_blink_timer->restart();
|
||||
break;
|
||||
default:
|
||||
dbgln("Cursor style not implemented");
|
||||
}
|
||||
m_cursor_style = style;
|
||||
invalidate_cursor();
|
||||
}
|
||||
|
||||
void TerminalWidget::context_menu_event(GUI::ContextMenuEvent& event)
|
||||
{
|
||||
if (m_hovered_href_id.is_null()) {
|
||||
|
@ -1107,5 +1155,4 @@ void TerminalWidget::set_font_and_resize_to_fit(const Gfx::Font& font)
|
|||
set_font(font);
|
||||
resize(widget_size_for_font(font));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -114,6 +114,7 @@ private:
|
|||
virtual void terminal_did_resize(u16 columns, u16 rows) override;
|
||||
virtual void terminal_history_changed() override;
|
||||
virtual void emit(const u8*, size_t) override;
|
||||
virtual void set_cursor_style(CursorStyle) override;
|
||||
|
||||
void set_logical_focus(bool);
|
||||
|
||||
|
@ -173,6 +174,8 @@ private:
|
|||
bool m_cursor_blink_state { true };
|
||||
bool m_automatic_size_policy { false };
|
||||
|
||||
VT::CursorStyle m_cursor_style { BlinkingBlock };
|
||||
|
||||
enum class AutoScrollDirection {
|
||||
None,
|
||||
Up,
|
||||
|
|
Loading…
Reference in a new issue