mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 01:47:36 +00:00
LibVT+Kernel: Create Color
class
Previously, we converted colors to their RGB values immediately when they were set. This meant that their semantic meaning was lost, we could not tell a precise RGB value apart from a named/indexed color. The new way of storing colors will allow us to retain this information, so we can change a color scheme on the fly, and previously emitted text will also be affected.
This commit is contained in:
parent
054c742d17
commit
99033876ec
7 changed files with 232 additions and 80 deletions
|
@ -17,6 +17,7 @@
|
|||
#include <Kernel/IO.h>
|
||||
#include <Kernel/StdLib.h>
|
||||
#include <Kernel/TTY/ConsoleManagement.h>
|
||||
#include <LibVT/Color.h>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <Kernel/Graphics/Console/Console.h>
|
||||
#include <Kernel/TTY/TTY.h>
|
||||
#include <LibVT/Attribute.h>
|
||||
#include <LibVT/Color.h>
|
||||
#include <LibVT/Position.h>
|
||||
#include <LibVT/Terminal.h>
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <AK/Noncopyable.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibVT/Color.h>
|
||||
#include <LibVT/XtermColors.h>
|
||||
|
||||
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;
|
||||
|
|
141
Userland/Libraries/LibVT/Color.h
Normal file
141
Userland/Libraries/LibVT/Color.h
Normal file
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Daniel Bertalan <dani@danielbertalan.dev>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Types.h>
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -9,6 +9,7 @@
|
|||
#include <AK/Debug.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <LibVT/Color.h>
|
||||
#include <LibVT/Terminal.h>
|
||||
#ifdef KERNEL
|
||||
# include <Kernel/TTY/VirtualConsole.h>
|
||||
|
@ -182,29 +183,33 @@ void Terminal::SGR(Parameters params)
|
|||
m_current_state.attribute.reset();
|
||||
return;
|
||||
}
|
||||
auto parse_color = [&]() -> Optional<u32> {
|
||||
auto parse_color = [&]() -> Optional<Color> {
|
||||
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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <LibGUI/Frame.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <LibVT/Color.h>
|
||||
#include <LibVT/Range.h>
|
||||
#include <LibVT/Terminal.h>
|
||||
|
||||
|
@ -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:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue