diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index ab1c1dc799..19f1637b11 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -139,10 +139,16 @@ set(ELF_SOURCES ../Libraries/LibELF/Validation.cpp ) +set(VT_SOURCES + ../Libraries/LibVT/Terminal.cpp + ../Libraries/LibVT/Line.cpp +) + set(SOURCES ${KERNEL_SOURCES} ${AK_SOURCES} ${ELF_SOURCES} + ${VT_SOURCES} ) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DKERNEL") diff --git a/Kernel/Console.cpp b/Kernel/Console.cpp index fa58813fb5..9f91261961 100644 --- a/Kernel/Console.cpp +++ b/Kernel/Console.cpp @@ -70,8 +70,6 @@ ssize_t Console::write(Kernel::FileDescription&, size_t, const u8* data, ssize_t { if (!size) return 0; - if (!m_implementation) - return 0; for (ssize_t i = 0; i < size; ++i) put_char(data[i]); return size; @@ -84,10 +82,4 @@ void Console::put_char(char ch) IO::out8(0xe9, ch); #endif m_logbuffer.enqueue(ch); - if (m_implementation) - m_implementation->on_sysconsole_receive(ch); -} - -ConsoleImplementation::~ConsoleImplementation() -{ } diff --git a/Kernel/Console.h b/Kernel/Console.h index 0e57be5e79..ff1ce9d356 100644 --- a/Kernel/Console.h +++ b/Kernel/Console.h @@ -30,12 +30,6 @@ #include #include -class ConsoleImplementation { -public: - virtual ~ConsoleImplementation(); - virtual void on_sysconsole_receive(u8) = 0; -}; - class Console final : public Kernel::CharacterDevice { AK_MAKE_ETERNAL public: @@ -52,16 +46,10 @@ public: virtual ssize_t write(Kernel::FileDescription&, size_t, const u8*, ssize_t) override; virtual const char* class_name() const override { return "Console"; } - void set_implementation(ConsoleImplementation* implementation) - { - m_implementation = implementation; - } - void put_char(char); const CircularQueue& logbuffer() const { return m_logbuffer; } private: - ConsoleImplementation* m_implementation { nullptr }; CircularQueue m_logbuffer; }; diff --git a/Kernel/TTY/VirtualConsole.cpp b/Kernel/TTY/VirtualConsole.cpp index 731335b564..3716421bfe 100644 --- a/Kernel/TTY/VirtualConsole.cpp +++ b/Kernel/TTY/VirtualConsole.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2018-2020, Andreas Kling + * Copyright (c) 2020 Sergey Bugaev * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,20 +39,9 @@ static u8* s_vga_buffer; static VirtualConsole* s_consoles[6]; static int s_active_console; -void VirtualConsole::get_vga_cursor(u8& row, u8& column) -{ - u16 value; - IO::out8(0x3d4, 0x0e); - value = IO::in8(0x3d5) << 8; - IO::out8(0x3d4, 0x0f); - value |= IO::in8(0x3d5); - row = value / columns(); - column = value % columns(); -} - void VirtualConsole::flush_vga_cursor() { - u16 value = m_current_vga_start_address + (m_cursor_row * columns() + m_cursor_column); + u16 value = m_current_vga_start_address + (m_terminal.cursor_row() * columns() + m_terminal.cursor_column()); IO::out8(0x3d4, 0x0e); IO::out8(0x3d5, MSB(value)); IO::out8(0x3d4, 0x0f); @@ -61,7 +51,6 @@ void VirtualConsole::flush_vga_cursor() void VirtualConsole::initialize() { s_vga_buffer = (u8*)0xc00b8000; - memset(s_consoles, 0, sizeof(s_consoles)); s_active_console = -1; } @@ -73,28 +62,15 @@ void VirtualConsole::set_graphical(bool graphical) m_graphical = graphical; } -VirtualConsole::VirtualConsole(unsigned index, InitialContents initial_contents) +VirtualConsole::VirtualConsole(unsigned index) : TTY(4, index) , m_index(index) + , m_terminal(*this) { - sprintf(m_tty_name, "/dev/tty%u", m_index); - set_size(80, 25); - m_horizontal_tabs = static_cast(kmalloc_eternal(columns())); - for (unsigned i = 0; i < columns(); ++i) - m_horizontal_tabs[i] = (i % 8) == 0; - // Rightmost column is always last tab on line. - m_horizontal_tabs[columns() - 1] = 1; + m_tty_name = String::format("/dev/tty%u", m_index); + m_terminal.set_size(80, 25); s_consoles[index] = this; - m_buffer = (u8*)kmalloc_eternal(rows() * columns() * 2); - if (initial_contents == AdoptCurrentVGABuffer) { - memcpy(m_buffer, s_vga_buffer, rows() * columns() * 2); - get_vga_cursor(m_cursor_row, m_cursor_column); - } else { - u16* line_mem = reinterpret_cast(m_buffer); - for (u16 i = 0; i < rows() * columns(); ++i) - line_mem[i] = 0x0720; - } } VirtualConsole::~VirtualConsole() @@ -102,16 +78,6 @@ VirtualConsole::~VirtualConsole() ASSERT_NOT_REACHED(); } -void VirtualConsole::clear() -{ - u16* linemem = m_active ? (u16*)s_vga_buffer : (u16*)m_buffer; - for (u16 i = 0; i < rows() * columns(); ++i) - linemem[i] = 0x0720; - if (m_active) - set_vga_start_row(0); - set_cursor(0, 0); -} - void VirtualConsole::switch_to(unsigned index) { if ((int)index == s_active_console) @@ -125,50 +91,35 @@ void VirtualConsole::switch_to(unsigned index) // We won't know how to switch away from a graphical console until we // can set the video mode on our own. Just stop anyone from trying for // now. - if (active_console->is_graphical()) + if (active_console->is_graphical()) { + dbg() << "Cannot switch away from graphical console yet :("; return; + } active_console->set_active(false); } dbg() << "VC: Switch to " << index << " (" << s_consoles[index] << ")"; s_active_console = index; s_consoles[s_active_console]->set_active(true); - Console::the().set_implementation(s_consoles[s_active_console]); } -void VirtualConsole::set_active(bool b) +void VirtualConsole::set_active(bool active) { - if (b == m_active) + if (active == m_active) return; InterruptDisabler disabler; - m_active = b; - if (!m_active) { - memcpy(m_buffer, m_current_vga_window, rows() * columns() * 2); + m_active = active; + + if (active) { + set_vga_start_row(0); + KeyboardDevice::the().set_client(this); + + m_terminal.m_need_full_flush = true; + flush_dirty_lines(); + } else { KeyboardDevice::the().set_client(nullptr); - return; } - - memcpy(s_vga_buffer, m_buffer, rows() * columns() * 2); - set_vga_start_row(0); - flush_vga_cursor(); - - KeyboardDevice::the().set_client(this); -} - -inline bool is_valid_parameter_character(u8 ch) -{ - return ch >= 0x30 && ch <= 0x3f; -} - -inline bool is_valid_intermediate_character(u8 ch) -{ - return ch >= 0x20 && ch <= 0x2f; -} - -inline bool is_valid_final_character(u8 ch) -{ - return ch >= 0x40 && ch <= 0x7e; } enum class VGAColor : u8 { @@ -207,6 +158,7 @@ enum class ANSIColor : u8 { BrightMagenta, BrightCyan, White, + __Count, }; static inline VGAColor ansi_color_to_vga(ANSIColor color) @@ -244,165 +196,18 @@ static inline VGAColor ansi_color_to_vga(ANSIColor color) return VGAColor::BrightCyan; case ANSIColor::White: return VGAColor::White; - } - ASSERT_NOT_REACHED(); - return VGAColor::LightGray; -} - -static inline u8 ansi_color_to_vga(u8 color) -{ - return (u8)ansi_color_to_vga((ANSIColor)color); -} - -void VirtualConsole::escape$m(const Vector& params) -{ - for (auto param : params) { - switch (param) { - case 0: - // Reset - m_current_attribute = 0x07; - break; - case 1: - // Bold - m_current_attribute |= 8; - break; - case 30: - case 31: - case 32: - case 33: - case 34: - case 35: - case 36: - case 37: - // Foreground color - m_current_attribute &= ~0x7; - m_current_attribute |= ansi_color_to_vga(param - 30); - break; - case 40: - case 41: - case 42: - case 43: - case 44: - case 45: - case 46: - case 47: - // Background color - m_current_attribute &= ~0x70; - m_current_attribute |= ansi_color_to_vga(param - 30) << 8; - break; - } - } -} - -void VirtualConsole::escape$s(const Vector&) -{ - m_saved_cursor_row = m_cursor_row; - m_saved_cursor_column = m_cursor_column; -} - -void VirtualConsole::escape$u(const Vector&) -{ - set_cursor(m_saved_cursor_row, m_saved_cursor_column); -} - -void VirtualConsole::escape$H(const Vector& params) -{ - unsigned row = 1; - unsigned col = 1; - if (params.size() >= 1) - row = params[0]; - if (params.size() >= 2) - col = params[1]; - set_cursor(row - 1, col - 1); -} - -void VirtualConsole::escape$A(const Vector& params) -{ - int num = 1; - if (params.size() >= 1) - num = params[0]; - int new_row = (int)m_cursor_row - num; - if (new_row < 0) - new_row = 0; - set_cursor(new_row, m_cursor_column); -} - -void VirtualConsole::escape$D(const Vector& params) -{ - int num = 1; - if (params.size() >= 1) - num = params[0]; - int new_column = (int)m_cursor_column - num; - if (new_column < 0) - new_column = 0; - set_cursor(m_cursor_row, new_column); -} - -void VirtualConsole::escape$J(const Vector& params) -{ - int mode = 0; - if (params.size() >= 1) - mode = params[0]; - switch (mode) { - case 0: - // FIXME: Clear from cursor to end of screen. - ASSERT_NOT_REACHED(); - break; - case 1: - // FIXME: Clear from cursor to beginning of screen. - ASSERT_NOT_REACHED(); - break; - case 2: - clear(); - break; - case 3: - // FIXME: [3J should also clear the scrollback buffer. - clear(); - break; - } -} - -void VirtualConsole::execute_escape_sequence(u8 final) -{ - auto paramparts = String::copy(m_parameters).split(';'); - Vector params; - for (auto& parampart : paramparts) { - bool ok; - unsigned value = parampart.to_uint(ok); - if (!ok) { - // FIXME: Should we do something else? - return; - } - params.append(value); - } - switch (final) { - case 'A': - escape$A(params); - break; - case 'D': - escape$D(params); - break; - case 'H': - escape$H(params); - break; - case 'J': - escape$J(params); - break; - case 'm': - escape$m(params); - break; - case 's': - escape$s(params); - break; - case 'u': - escape$u(params); - break; default: - break; + ASSERT_NOT_REACHED(); } +} - m_parameters.clear(); - m_intermediates.clear(); +static inline u8 xterm_color_to_vga(u32 color) +{ + for (u8 i = 0; i < (u8)ANSIColor::__Count; i++) { + if (xterm_colors[i] == color) + return (u8)ansi_color_to_vga((ANSIColor)i); + } + return (u8)VGAColor::LightGray; } void VirtualConsole::clear_vga_row(u16 row) @@ -412,182 +217,37 @@ void VirtualConsole::clear_vga_row(u16 row) linemem[i] = 0x0720; } -void VirtualConsole::scroll_up() +void VirtualConsole::on_key_pressed(KeyboardDevice::Event event) { - if (m_cursor_row == (rows() - 1)) { - if (m_active) { - if (m_vga_start_row >= 160) { - memcpy(s_vga_buffer, m_current_vga_window + 160, (rows() - 1) * columns() * 2); - set_vga_start_row(0); - clear_vga_row(24); - } else { - set_vga_start_row(m_vga_start_row + 1); - clear_vga_row(24); - } - } else { - memmove(m_buffer, m_buffer + 160, 160 * 24); - u16* linemem = (u16*)&m_buffer[24 * 160]; - for (u16 i = 0; i < columns(); ++i) - linemem[i] = 0x0720; - } - } else { - ++m_cursor_row; - } - m_cursor_column = 0; -} - -void VirtualConsole::set_cursor(unsigned row, unsigned column) -{ - ASSERT(row < rows()); - ASSERT(column < columns()); - m_cursor_row = row; - m_cursor_column = column; - if (m_active) - flush_vga_cursor(); -} - -void VirtualConsole::put_character_at(unsigned row, unsigned column, u8 ch) -{ - ASSERT(row < rows()); - ASSERT(column < columns()); - u16 cur = (row * 160) + (column * 2); - if (m_active) { - u16 cur = (row * 160) + (column * 2); - m_current_vga_window[cur] = ch; - m_current_vga_window[cur + 1] = m_current_attribute; - } else { - m_buffer[cur] = ch; - m_buffer[cur + 1] = m_current_attribute; - } -} - -void VirtualConsole::on_char(u8 ch) -{ - // ignore writes in graphical mode + // Ignore keyboard in graphical mode. if (m_graphical) return; - switch (m_escape_state) { - case ExpectBracket: - if (ch == '[') - m_escape_state = ExpectParameter; - else - m_escape_state = Normal; + if (!event.is_press()) return; - case ExpectParameter: - if (is_valid_parameter_character(ch)) { - m_parameters.append(ch); - return; - } - m_escape_state = ExpectIntermediate; - [[fallthrough]]; - case ExpectIntermediate: - if (is_valid_intermediate_character(ch)) { - m_intermediates.append(ch); - return; - } - m_escape_state = ExpectFinal; - [[fallthrough]]; - case ExpectFinal: - if (is_valid_final_character(ch)) { - m_escape_state = Normal; - execute_escape_sequence(ch); - return; - } - m_escape_state = Normal; - return; - case Normal: - break; - } - switch (ch) { - case '\0': - return; - case '\033': - m_escape_state = ExpectBracket; - return; - case 8: // Backspace - if (m_cursor_column) { - set_cursor(m_cursor_row, m_cursor_column - 1); - put_character_at(m_cursor_row, m_cursor_column, ' '); - return; - } - break; - case '\a': - // FIXME: Bell! - return; - case '\t': { - for (unsigned i = m_cursor_column; i < columns(); ++i) { - if (m_horizontal_tabs[i]) { - set_cursor(m_cursor_row, i); - return; - } - } + if (event.key == KeyCode::Key_PageUp && event.flags == Mod_Shift) { + // TODO: scroll up return; } - case '\n': - scroll_up(); - set_cursor(m_cursor_row, m_cursor_column); + if (event.key == KeyCode::Key_PageDown && event.flags == Mod_Shift) { + // TODO: scroll down return; } - put_character_at(m_cursor_row, m_cursor_column, ch); - - ++m_cursor_column; - if (m_cursor_column >= columns()) - scroll_up(); - set_cursor(m_cursor_row, m_cursor_column); -} - -void VirtualConsole::on_key_pressed(KeyboardDevice::Event key) -{ - // ignore keyboard in graphical mode - if (m_graphical) - return; - - if (!key.is_press()) - return; - if (key.ctrl()) { - if (key.character >= 'a' && key.character <= 'z') { - emit(key.character - 'a' + 1); - return; - } else if (key.character == '\\') { - emit(0x1c); - return; - } - } - emit(key.character); -} - -void VirtualConsole::on_sysconsole_receive(u8 ch) -{ - InterruptDisabler disabler; - auto old_attribute = m_current_attribute; - m_current_attribute = 0x03; - on_char(ch); - m_current_attribute = old_attribute; + m_terminal.handle_key_press(event.key, event.character, event.flags); } ssize_t VirtualConsole::on_tty_write(const u8* data, ssize_t size) { InterruptDisabler disabler; for (ssize_t i = 0; i < size; ++i) - on_char(data[i]); + m_terminal.on_input(data[i]); + if (m_active) + flush_dirty_lines(); return size; } -StringView VirtualConsole::tty_name() const -{ - return m_tty_name; -} - -void VirtualConsole::echo(u8 ch) -{ - if (should_echo_input()) { - on_tty_write(&ch, 1); - } -} - void VirtualConsole::set_vga_start_row(u16 row) { m_vga_start_row = row; @@ -599,4 +259,77 @@ void VirtualConsole::set_vga_start_row(u16 row) IO::out8(0x3d5, LSB(m_current_vga_start_address)); } +static inline u8 attribute_to_vga(const VT::Attribute& attribute) +{ + u8 vga_attr = 0x07; + + if (attribute.flags & VT::Attribute::Bold) + vga_attr |= 0x08; + + // Background color + vga_attr &= ~0x70; + vga_attr |= xterm_color_to_vga(attribute.background_color) << 8; + + // Foreground color + vga_attr &= ~0x7; + vga_attr |= xterm_color_to_vga(attribute.foreground_color); + + return vga_attr; +} + +void VirtualConsole::flush_dirty_lines() +{ + for (u16 visual_row = 0; visual_row < m_terminal.rows(); ++visual_row) { + auto& line = m_terminal.visible_line(visual_row); + if (!line.is_dirty() && !m_terminal.m_need_full_flush) + continue; + for (size_t column = 0; column < line.length(); ++column) { + u32 codepoint = line.codepoint(column); + auto attribute = line.attributes()[column]; + u16 vga_index = (visual_row * 160) + (column * 2); + m_current_vga_window[vga_index] = codepoint < 128 ? codepoint : '?'; + m_current_vga_window[vga_index + 1] = attribute_to_vga(attribute); + } + line.set_dirty(false); + } + flush_vga_cursor(); + m_terminal.m_need_full_flush = false; +} + +void VirtualConsole::beep() +{ + // TODO + dbg() << "Beep!1"; +} + +void VirtualConsole::set_window_title(const StringView&) +{ + // Do nothing. +} + +void VirtualConsole::terminal_did_resize(u16 columns, u16 rows) +{ + ASSERT(columns == 80); + ASSERT(rows == 25); + set_size(columns, rows); +} + +void VirtualConsole::terminal_history_changed() +{ + // Do nothing, I guess? +} + +void VirtualConsole::emit(const u8* data, size_t size) +{ + for (size_t i = 0; i < size; i++) + TTY::emit(data[i]); +} + +void VirtualConsole::echo(u8 ch) +{ + if (should_echo_input()) { + on_tty_write(&ch, 1); + } +} + } diff --git a/Kernel/TTY/VirtualConsole.h b/Kernel/TTY/VirtualConsole.h index 8f6745b81e..db9c9b8b01 100644 --- a/Kernel/TTY/VirtualConsole.h +++ b/Kernel/TTY/VirtualConsole.h @@ -26,23 +26,19 @@ #pragma once +#include #include #include -#include +#include namespace Kernel { class VirtualConsole final : public TTY , public KeyboardClient - , public ConsoleImplementation { + , public VT::TerminalClient { AK_MAKE_ETERNAL public: - enum InitialContents { - Cleared, - AdoptCurrentVGABuffer - }; - - VirtualConsole(unsigned index, InitialContents = Cleared); + VirtualConsole(unsigned index); virtual ~VirtualConsole() override; static void switch_to(unsigned); @@ -55,68 +51,39 @@ private: // ^KeyboardClient virtual void on_key_pressed(KeyboardDevice::Event) override; - // ^ConsoleImplementation - virtual void on_sysconsole_receive(u8) override; - // ^TTY virtual ssize_t on_tty_write(const u8*, ssize_t) override; - virtual StringView tty_name() const override; + virtual StringView tty_name() const override { return m_tty_name; } virtual void echo(u8) override; + // ^TerminalClient + virtual void beep() override; + virtual void set_window_title(const StringView&) override; + virtual void terminal_did_resize(u16 columns, u16 rows) override; + virtual void terminal_history_changed() override; + virtual void emit(const u8*, size_t) override; + // ^CharacterDevice virtual const char* class_name() const override { return "VirtualConsole"; } void set_active(bool); - void on_char(u8); - void get_vga_cursor(u8& row, u8& column); void flush_vga_cursor(); + void flush_dirty_lines(); - u8* m_buffer; unsigned m_index; bool m_active { false }; bool m_graphical { false }; - void scroll_up(); - void set_cursor(unsigned row, unsigned column); - void put_character_at(unsigned row, unsigned column, u8 ch); - - void escape$A(const Vector&); - void escape$D(const Vector&); - void escape$H(const Vector&); - void escape$J(const Vector&); - void escape$m(const Vector&); - void escape$s(const Vector&); - void escape$u(const Vector&); - - void clear(); - - u8 m_cursor_row { 0 }; - u8 m_cursor_column { 0 }; - u8 m_saved_cursor_row { 0 }; - u8 m_saved_cursor_column { 0 }; - u8 m_current_attribute { 0x07 }; - void clear_vga_row(u16 row); void set_vga_start_row(u16 row); u16 m_vga_start_row { 0 }; u16 m_current_vga_start_address { 0 }; u8* m_current_vga_window { nullptr }; - void execute_escape_sequence(u8 final); + VT::Terminal m_terminal; - enum EscapeState { - Normal, - ExpectBracket, - ExpectParameter, - ExpectIntermediate, - ExpectFinal, - }; - EscapeState m_escape_state { Normal }; - Vector m_parameters; - Vector m_intermediates; - u8* m_horizontal_tabs { nullptr }; - char m_tty_name[32]; + String m_tty_name; }; } diff --git a/Kernel/init.cpp b/Kernel/init.cpp index 32cfacd715..68f60bcf5b 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -143,7 +143,7 @@ extern "C" [[noreturn]] void init() new SerialDevice(SERIAL_COM4_ADDR, 67); VirtualConsole::initialize(); - tty0 = new VirtualConsole(0, VirtualConsole::AdoptCurrentVGABuffer); + tty0 = new VirtualConsole(0); new VirtualConsole(1); VirtualConsole::switch_to(0);