diff options
author | Sergey Bugaev <bugaevc@serenityos.org> | 2020-05-27 00:35:14 +0300 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-05-27 11:19:38 +0200 |
commit | f11270e7ce37989a5c99047e0a4ad6aeaef21d13 (patch) | |
tree | 431f4f1c34d83348bca36629465c72b0cc39186e /Kernel/TTY | |
parent | 620697d924c15d62719ced9b4a2cab909bec74cb (diff) | |
download | serenity-f11270e7ce37989a5c99047e0a4ad6aeaef21d13.zip |
Kernel: Port VirtualConsole to LibVT :^)
Unfortunately this drops the feature of preserving VGA buffer contents.
Resolves https://github.com/SerenityOS/serenity/issues/2399
Diffstat (limited to 'Kernel/TTY')
-rw-r--r-- | Kernel/TTY/VirtualConsole.cpp | 473 | ||||
-rw-r--r-- | Kernel/TTY/VirtualConsole.h | 65 |
2 files changed, 119 insertions, 419 deletions
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 <kling@serenityos.org> + * Copyright (c) 2020 Sergey Bugaev <bugaevc@serenityos.org> * 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<u8*>(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<u16*>(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); - 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); -} + m_active = active; -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; -} + if (active) { + set_vga_start_row(0); + KeyboardDevice::the().set_client(this); -inline bool is_valid_final_character(u8 ch) -{ - return ch >= 0x40 && ch <= 0x7e; + m_terminal.m_need_full_flush = true; + flush_dirty_lines(); + } else { + KeyboardDevice::the().set_client(nullptr); + } } 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<unsigned>& 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<unsigned>&) -{ - m_saved_cursor_row = m_cursor_row; - m_saved_cursor_column = m_cursor_column; -} - -void VirtualConsole::escape$u(const Vector<unsigned>&) -{ - set_cursor(m_saved_cursor_row, m_saved_cursor_column); -} - -void VirtualConsole::escape$H(const Vector<unsigned>& 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<unsigned>& 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<unsigned>& 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<unsigned>& 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. + default: ASSERT_NOT_REACHED(); - break; - case 2: - clear(); - break; - case 3: - // FIXME: <esc>[3J should also clear the scrollback buffer. - clear(); - break; } } -void VirtualConsole::execute_escape_sequence(u8 final) +static inline u8 xterm_color_to_vga(u32 color) { - auto paramparts = String::copy(m_parameters).split(';'); - Vector<unsigned> 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); + for (u8 i = 0; i < (u8)ANSIColor::__Count; i++) { + if (xterm_colors[i] == color) + return (u8)ansi_color_to_vga((ANSIColor)i); } - 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; - } - - m_parameters.clear(); - m_intermediates.clear(); + return (u8)VGAColor::LightGray; } void VirtualConsole::clear_vga_row(u16 row) @@ -412,173 +217,112 @@ 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; + // Ignore keyboard in graphical mode. + if (m_graphical) + return; + + if (!event.is_press()) + return; + + if (event.key == KeyCode::Key_PageUp && event.flags == Mod_Shift) { + // TODO: scroll up + return; + } + if (event.key == KeyCode::Key_PageDown && event.flags == Mod_Shift) { + // TODO: scroll down + return; } - m_cursor_column = 0; + + m_terminal.handle_key_press(event.key, event.character, event.flags); } -void VirtualConsole::set_cursor(unsigned row, unsigned column) +ssize_t VirtualConsole::on_tty_write(const u8* data, ssize_t size) { - ASSERT(row < rows()); - ASSERT(column < columns()); - m_cursor_row = row; - m_cursor_column = column; + InterruptDisabler disabler; + for (ssize_t i = 0; i < size; ++i) + m_terminal.on_input(data[i]); if (m_active) - flush_vga_cursor(); + flush_dirty_lines(); + return size; } -void VirtualConsole::put_character_at(unsigned row, unsigned column, u8 ch) +void VirtualConsole::set_vga_start_row(u16 row) { - 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; - } + m_vga_start_row = row; + m_current_vga_start_address = row * columns(); + m_current_vga_window = s_vga_buffer + row * 160; + IO::out8(0x3d4, 0x0c); + IO::out8(0x3d5, MSB(m_current_vga_start_address)); + IO::out8(0x3d4, 0x0d); + IO::out8(0x3d5, LSB(m_current_vga_start_address)); } -void VirtualConsole::on_char(u8 ch) +static inline u8 attribute_to_vga(const VT::Attribute& attribute) { - // ignore writes in graphical mode - if (m_graphical) - return; + u8 vga_attr = 0x07; - switch (m_escape_state) { - case ExpectBracket: - if (ch == '[') - m_escape_state = ExpectParameter; - else - m_escape_state = Normal; - 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; - } + if (attribute.flags & VT::Attribute::Bold) + vga_attr |= 0x08; - 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; - } - } - return; - } - case '\n': - scroll_up(); - set_cursor(m_cursor_row, m_cursor_column); - return; - } + // Background color + vga_attr &= ~0x70; + vga_attr |= xterm_color_to_vga(attribute.background_color) << 8; - put_character_at(m_cursor_row, m_cursor_column, ch); + // Foreground color + vga_attr &= ~0x7; + vga_attr |= xterm_color_to_vga(attribute.foreground_color); - ++m_cursor_column; - if (m_cursor_column >= columns()) - scroll_up(); - set_cursor(m_cursor_row, m_cursor_column); + return vga_attr; } -void VirtualConsole::on_key_pressed(KeyboardDevice::Event key) +void VirtualConsole::flush_dirty_lines() { - // 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; + 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); } - emit(key.character); + flush_vga_cursor(); + m_terminal.m_need_full_flush = false; } -void VirtualConsole::on_sysconsole_receive(u8 ch) +void VirtualConsole::beep() { - InterruptDisabler disabler; - auto old_attribute = m_current_attribute; - m_current_attribute = 0x03; - on_char(ch); - m_current_attribute = old_attribute; + // TODO + dbg() << "Beep!1"; } -ssize_t VirtualConsole::on_tty_write(const u8* data, ssize_t size) +void VirtualConsole::set_window_title(const StringView&) { - InterruptDisabler disabler; - for (ssize_t i = 0; i < size; ++i) - on_char(data[i]); - return size; + // Do nothing. } -StringView VirtualConsole::tty_name() const +void VirtualConsole::terminal_did_resize(u16 columns, u16 rows) { - return m_tty_name; + 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) @@ -588,15 +332,4 @@ void VirtualConsole::echo(u8 ch) } } -void VirtualConsole::set_vga_start_row(u16 row) -{ - m_vga_start_row = row; - m_current_vga_start_address = row * columns(); - m_current_vga_window = s_vga_buffer + row * 160; - IO::out8(0x3d4, 0x0c); - IO::out8(0x3d5, MSB(m_current_vga_start_address)); - IO::out8(0x3d4, 0x0d); - IO::out8(0x3d5, LSB(m_current_vga_start_address)); -} - } 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 <Kernel/Console.h> #include <Kernel/Devices/KeyboardDevice.h> #include <Kernel/TTY/TTY.h> -#include <Kernel/Console.h> +#include <LibVT/Terminal.h> 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<unsigned>&); - void escape$D(const Vector<unsigned>&); - void escape$H(const Vector<unsigned>&); - void escape$J(const Vector<unsigned>&); - void escape$m(const Vector<unsigned>&); - void escape$s(const Vector<unsigned>&); - void escape$u(const Vector<unsigned>&); - - 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); - - enum EscapeState { - Normal, - ExpectBracket, - ExpectParameter, - ExpectIntermediate, - ExpectFinal, - }; - EscapeState m_escape_state { Normal }; - Vector<u8> m_parameters; - Vector<u8> m_intermediates; - u8* m_horizontal_tabs { nullptr }; - char m_tty_name[32]; + VT::Terminal m_terminal; + + String m_tty_name; }; } |