diff --git a/Kernel/TTY/VirtualConsole.cpp b/Kernel/TTY/VirtualConsole.cpp index fe77f24704..ceef83cd0a 100644 --- a/Kernel/TTY/VirtualConsole.cpp +++ b/Kernel/TTY/VirtualConsole.cpp @@ -17,6 +17,7 @@ #include #include #include +#include namespace Kernel { @@ -205,73 +206,55 @@ UNMAP_AFTER_INIT VirtualConsole::~VirtualConsole() VERIFY_NOT_REACHED(); } -enum class ANSIColor : u8 { - Black = 0, - Red, - Green, - Brown, - Blue, - Magenta, - Cyan, - LightGray, - DarkGray, - BrightRed, - BrightGreen, - Yellow, - BrightBlue, - BrightMagenta, - BrightCyan, - White, - __Count, -}; - -static inline Graphics::Console::Color ansi_color_to_standard_vga_color(ANSIColor color) +static inline Graphics::Console::Color ansi_color_to_standard_vga_color(VT::Color::ANSIColor color) { switch (color) { - case ANSIColor::Black: + case VT::Color::ANSIColor::DefaultBackground: + case VT::Color::ANSIColor::Black: return Graphics::Console::Color::Black; - case ANSIColor::Red: + case VT::Color::ANSIColor::Red: return Graphics::Console::Color::Red; - case ANSIColor::Brown: + case VT::Color::ANSIColor::Green: + return Graphics::Console::Green; + case VT::Color::ANSIColor::Yellow: + // VGA only has bright yellow, and treats normal yellow as a brownish orange color. return Graphics::Console::Color::Brown; - case ANSIColor::Blue: + case VT::Color::ANSIColor::Blue: return Graphics::Console::Color::Blue; - case ANSIColor::Magenta: + case VT::Color::ANSIColor::Magenta: return Graphics::Console::Color::Magenta; - case ANSIColor::Green: - return Graphics::Console::Color::Green; - case ANSIColor::Cyan: + case VT::Color::ANSIColor::Cyan: return Graphics::Console::Color::Cyan; - case ANSIColor::LightGray: - return Graphics::Console::Color::LightGray; - case ANSIColor::DarkGray: - return Graphics::Console::Color::DarkGray; - case ANSIColor::BrightRed: - return Graphics::Console::Color::BrightRed; - case ANSIColor::BrightGreen: - return Graphics::Console::Color::BrightGreen; - case ANSIColor::Yellow: - return Graphics::Console::Color::Yellow; - case ANSIColor::BrightBlue: - return Graphics::Console::Color::BrightBlue; - case ANSIColor::BrightMagenta: - return Graphics::Console::Color::BrightMagenta; - case ANSIColor::BrightCyan: - return Graphics::Console::Color::BrightCyan; - case ANSIColor::White: + case VT::Color::ANSIColor::DefaultForeground: + case VT::Color::ANSIColor::White: return Graphics::Console::Color::White; + case VT::Color::ANSIColor::BrightBlack: + return Graphics::Console::Color::LightGray; + case VT::Color::ANSIColor::BrightRed: + return Graphics::Console::Color::BrightRed; + case VT::Color::ANSIColor::BrightGreen: + return Graphics::Console::Color::BrightGreen; + case VT::Color::ANSIColor::BrightYellow: + return Graphics::Console::Color::Yellow; + case VT::Color::ANSIColor::BrightBlue: + return Graphics::Console::Color::BrightBlue; + case VT::Color::ANSIColor::BrightMagenta: + return Graphics::Console::Color::BrightMagenta; + case VT::Color::ANSIColor::BrightCyan: + return Graphics::Console::Color::BrightCyan; default: VERIFY_NOT_REACHED(); } } -static inline Graphics::Console::Color xterm_to_standard_color(u32 color) +static inline Graphics::Console::Color terminal_to_standard_color(VT::Color color) { - for (u8 i = 0; i < (u8)ANSIColor::__Count; i++) { - if (xterm_colors[i] == color) - return (Graphics::Console::Color)ansi_color_to_standard_vga_color((ANSIColor)i); + switch (color.kind()) { + case VT::Color::Kind::Named: + return ansi_color_to_standard_vga_color(color.as_named()); + default: + return Graphics::Console::Color::LightGray; } - return Graphics::Console::Color::LightGray; } void VirtualConsole::on_key_pressed(KeyEvent event) @@ -338,13 +321,13 @@ void VirtualConsole::flush_dirty_lines() for (size_t column = 0; column < columns(); ++column) { auto& cell = cell_at(column, visual_row); - auto foreground_color = xterm_to_standard_color(cell.attribute.effective_foreground_color()); + auto foreground_color = terminal_to_standard_color(cell.attribute.effective_foreground_color()); if (cell.attribute.flags & VT::Attribute::Flags::Bold) foreground_color = (Graphics::Console::Color)((u8)foreground_color | 0x08); GraphicsManagement::the().console()->write(column, visual_row, ((u8)cell.ch < 128 ? cell.ch : '?'), - xterm_to_standard_color(cell.attribute.effective_background_color()), + terminal_to_standard_color(cell.attribute.effective_background_color()), foreground_color); } line.dirty = false; diff --git a/Kernel/TTY/VirtualConsole.h b/Kernel/TTY/VirtualConsole.h index 455a5bc2b3..fe0b62a23c 100644 --- a/Kernel/TTY/VirtualConsole.h +++ b/Kernel/TTY/VirtualConsole.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include diff --git a/Userland/Libraries/LibVT/Attribute.h b/Userland/Libraries/LibVT/Attribute.h index c5d154e5c0..f63f0aab20 100644 --- a/Userland/Libraries/LibVT/Attribute.h +++ b/Userland/Libraries/LibVT/Attribute.h @@ -9,6 +9,7 @@ #include #include #include +#include #include namespace VT { @@ -16,8 +17,8 @@ namespace VT { struct Attribute { Attribute() { reset(); } - static const u32 default_foreground_color = xterm_colors[7]; - static const u32 default_background_color = xterm_colors[0]; + static constexpr Color default_foreground_color = Color::named(Color::ANSIColor::DefaultForeground); + static constexpr Color default_background_color = Color::named(Color::ANSIColor::DefaultBackground); void reset() { @@ -25,11 +26,11 @@ struct Attribute { background_color = default_background_color; flags = Flags::NoAttributes; } - u32 foreground_color {}; - u32 background_color {}; + Color foreground_color { default_foreground_color }; + Color background_color { default_background_color }; - u32 effective_background_color() const { return flags & Negative ? foreground_color : background_color; } - u32 effective_foreground_color() const { return flags & Negative ? background_color : foreground_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; } #ifndef KERNEL String href; diff --git a/Userland/Libraries/LibVT/Color.h b/Userland/Libraries/LibVT/Color.h new file mode 100644 index 0000000000..a180965933 --- /dev/null +++ b/Userland/Libraries/LibVT/Color.h @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2021, Daniel Bertalan + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace VT { + +class Color { +public: + enum class ANSIColor : u16 { + Black = 0, + Red, + Green, + Yellow, + Blue, + Magenta, + Cyan, + White, + BrightBlack, + BrightRed, + BrightGreen, + BrightYellow, + BrightBlue, + BrightMagenta, + BrightCyan, + BrightWhite, + // We use the values above to directly index into the color lookup table, + // but the ones below are handled separately. + DefaultForeground = 256, + DefaultBackground + }; + + static constexpr Color rgb(u32 rgb) + { + return Color(rgb); + } + + static constexpr Color indexed(u8 index) + { + return Color(index); + } + + static constexpr Color named(ANSIColor name) + { + return Color(name); + } + + constexpr bool is_rgb() const + { + return m_kind == Kind::RGB; + } + + constexpr bool is_indexed() const + { + return m_kind == Kind::Indexed; + } + + constexpr bool is_named() const + { + return m_kind == Kind::Named; + } + + constexpr u32 as_rgb() const + { + VERIFY(is_rgb()); + return m_value.as_rgb; + } + + constexpr u8 as_indexed() const + { + VERIFY(is_indexed()); + return m_value.as_indexed; + } + + constexpr ANSIColor as_named() const + { + VERIFY(is_named()); + return m_value.as_named; + } + + constexpr bool operator==(const Color& other) const + { + if (m_kind != other.kind()) + return false; + + switch (m_kind) { + case RGB: + return m_value.as_rgb == other.as_rgb(); + case Indexed: + return m_value.as_indexed == other.as_indexed(); + case Named: + return m_value.as_named == other.as_named(); + default: + VERIFY_NOT_REACHED(); + }; + } + + enum Kind { + RGB, + Indexed, + Named + }; + + constexpr Kind kind() const + { + return m_kind; + } + +private: + Kind m_kind; + + union { + u32 as_rgb; + u8 as_indexed; + ANSIColor as_named; + } m_value; + + constexpr Color(u32 rgb) + : m_kind(Kind::RGB) + { + m_value.as_rgb = rgb; + } + + constexpr Color(u8 index) + : m_kind(Kind::Indexed) + { + m_value.as_indexed = index; + } + + constexpr Color(ANSIColor name) + : m_kind(Kind::Named) + { + m_value.as_named = name; + } +}; +} diff --git a/Userland/Libraries/LibVT/Terminal.cpp b/Userland/Libraries/LibVT/Terminal.cpp index 6ee4fcbc9a..e6229857f2 100644 --- a/Userland/Libraries/LibVT/Terminal.cpp +++ b/Userland/Libraries/LibVT/Terminal.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #ifdef KERNEL # include @@ -182,29 +183,33 @@ void Terminal::SGR(Parameters params) m_current_state.attribute.reset(); return; } - auto parse_color = [&]() -> Optional { + auto parse_color = [&]() -> Optional { if (params.size() < 2) { dbgln("Color code has no type"); return {}; } - u32 color = 0; + u32 rgb = 0; switch (params[1]) { case 5: // 8-bit if (params.size() < 3) { dbgln("8-bit color code has too few parameters"); return {}; } - return xterm_colors[params[2]]; + if (params[2] > 255) { + dbgln("8-bit color code has out-of-bounds value"); + return {}; + } + return Color::indexed(params[2]); case 2: // 24-bit if (params.size() < 5) { dbgln("24-bit color code has too few parameters"); return {}; } for (size_t i = 0; i < 3; ++i) { - color <<= 8; - color |= params[i + 2]; + rgb <<= 8; + rgb |= params[i + 2]; } - return color; + return Color::rgb(rgb); default: dbgln("Unknown color type {}", params[1]); return {}; @@ -264,7 +269,7 @@ void Terminal::SGR(Parameters params) // Foreground color if (m_current_state.attribute.flags & Attribute::Bold) param += 8; - m_current_state.attribute.foreground_color = xterm_colors[param - 30]; + m_current_state.attribute.foreground_color = Color::named((Color::ANSIColor)(param - 30)); break; case 39: // reset foreground @@ -281,7 +286,7 @@ void Terminal::SGR(Parameters params) // Background color if (m_current_state.attribute.flags & Attribute::Bold) param += 8; - m_current_state.attribute.background_color = xterm_colors[param - 40]; + m_current_state.attribute.background_color = Color::named((Color::ANSIColor)(param - 40)); break; case 49: // reset background diff --git a/Userland/Libraries/LibVT/TerminalWidget.cpp b/Userland/Libraries/LibVT/TerminalWidget.cpp index cc93514625..6998a8a96d 100644 --- a/Userland/Libraries/LibVT/TerminalWidget.cpp +++ b/Userland/Libraries/LibVT/TerminalWidget.cpp @@ -153,11 +153,6 @@ TerminalWidget::~TerminalWidget() { } -static inline Color color_from_rgb(unsigned color) -{ - return Color::from_rgb(color); -} - Gfx::IntRect TerminalWidget::glyph_rect(u16 row, u16 column) { int y = row * m_line_height; @@ -275,9 +270,9 @@ void TerminalWidget::paint_event(GUI::PaintEvent& event) painter.add_clip_rect(terminal_buffer_rect); if (visual_beep_active) - painter.clear_rect(frame_inner_rect(), Color::Red); + painter.clear_rect(frame_inner_rect(), terminal_color_to_rgb(VT::Color::named(VT::Color::ANSIColor::Red))); else - painter.clear_rect(frame_inner_rect(), Color(Color::Black).with_alpha(m_opacity)); + painter.clear_rect(frame_inner_rect(), terminal_color_to_rgb(VT::Color::named(VT::Color::ANSIColor::Black)).with_alpha(m_opacity)); invalidate_cursor(); int rows_from_history = 0; @@ -320,9 +315,9 @@ void TerminalWidget::paint_event(GUI::PaintEvent& event) auto& line = m_terminal.line(first_row_from_history + visual_row); bool has_only_one_background_color = line.has_only_one_background_color(); if (visual_beep_active) - painter.clear_rect(row_rect, Color::Red); + painter.clear_rect(row_rect, terminal_color_to_rgb(VT::Color::named(VT::Color::ANSIColor::Red))); else if (has_only_one_background_color) - painter.clear_rect(row_rect, color_from_rgb(line.attribute_at(0).effective_background_color()).with_alpha(m_opacity)); + painter.clear_rect(row_rect, terminal_color_to_rgb(line.attribute_at(0).effective_background_color()).with_alpha(m_opacity)); for (size_t column = 0; column < line.length(); ++column) { bool should_reverse_fill_for_cursor_or_selection = m_cursor_blink_state @@ -334,9 +329,9 @@ void TerminalWidget::paint_event(GUI::PaintEvent& event) auto attribute = line.attribute_at(column); auto character_rect = glyph_rect(visual_row, column); auto cell_rect = character_rect.inflated(0, m_line_spacing); - auto text_color = color_from_rgb(should_reverse_fill_for_cursor_or_selection ? attribute.effective_background_color() : attribute.effective_foreground_color()); + auto text_color = terminal_color_to_rgb(should_reverse_fill_for_cursor_or_selection ? attribute.effective_background_color() : attribute.effective_foreground_color()); if ((!visual_beep_active && !has_only_one_background_color) || should_reverse_fill_for_cursor_or_selection) - painter.clear_rect(cell_rect, color_from_rgb(should_reverse_fill_for_cursor_or_selection ? attribute.effective_foreground_color() : attribute.effective_background_color())); + painter.clear_rect(cell_rect, terminal_color_to_rgb(should_reverse_fill_for_cursor_or_selection ? attribute.effective_foreground_color() : attribute.effective_background_color())); enum class UnderlineStyle { None, @@ -398,7 +393,7 @@ void TerminalWidget::paint_event(GUI::PaintEvent& event) && visual_row == row_with_cursor && column == m_terminal.cursor_column(); should_reverse_fill_for_cursor_or_selection |= selection_contains({ first_row_from_history + visual_row, (int)column }); - auto text_color = color_from_rgb(should_reverse_fill_for_cursor_or_selection ? attribute.effective_background_color() : attribute.effective_foreground_color()); + auto text_color = terminal_color_to_rgb(should_reverse_fill_for_cursor_or_selection ? attribute.effective_background_color() : attribute.effective_foreground_color()); u32 code_point = line.code_point(column); if (code_point == ' ') @@ -427,7 +422,7 @@ void TerminalWidget::paint_event(GUI::PaintEvent& event) 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 cursor_color = terminal_color_to_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(); @@ -1148,6 +1143,29 @@ Gfx::IntSize TerminalWidget::widget_size_for_font(const Gfx::Font& font) const }; } +Gfx::Color TerminalWidget::terminal_color_to_rgb(VT::Color color) +{ + switch (color.kind()) { + case VT::Color::Kind::RGB: + return Gfx::Color::from_rgb(color.as_rgb()); + case VT::Color::Kind::Indexed: + return Gfx::Color::from_rgb(xterm_colors[color.as_indexed()]); + case VT::Color::Kind::Named: { + auto ansi = color.as_named(); + if ((u16)ansi < 256) + return Gfx::Color::from_rgb(xterm_colors[(u16)ansi]); + else if (ansi == VT::Color::ANSIColor::DefaultForeground) + return Gfx::Color::from_rgb(xterm_colors[7]); + else if (ansi == VT::Color::ANSIColor::DefaultBackground) + return Gfx::Color::from_rgb(xterm_colors[0]); + else + VERIFY_NOT_REACHED(); + } + default: + VERIFY_NOT_REACHED(); + } +}; + void TerminalWidget::set_font_and_resize_to_fit(const Gfx::Font& font) { set_font(font); diff --git a/Userland/Libraries/LibVT/TerminalWidget.h b/Userland/Libraries/LibVT/TerminalWidget.h index 7d658969b4..39d8a7f2e9 100644 --- a/Userland/Libraries/LibVT/TerminalWidget.h +++ b/Userland/Libraries/LibVT/TerminalWidget.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -86,6 +87,8 @@ public: GUI::Menu& context_menu() { return *m_context_menu; } + Gfx::Color terminal_color_to_rgb(VT::Color); + void set_font_and_resize_to_fit(const Gfx::Font&); private: