diff options
author | Andreas Kling <awesomekling@gmail.com> | 2018-10-30 13:59:29 +0100 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2018-10-30 13:59:29 +0100 |
commit | 68739dc43e6bc42f9cac79fe5cbec714ddeeb218 (patch) | |
tree | 705579b6a757d6219d28e6a8c2a4244eb828bcaa | |
parent | bd2b5327d042f5bf04ae7089d8b36c113313f8f3 (diff) | |
download | serenity-68739dc43e6bc42f9cac79fe5cbec714ddeeb218.zip |
Start working on virtual consoles/TTYs.
This is a mess right now, but I'd rather commit as I go.
-rw-r--r-- | Kernel/Console.cpp | 300 | ||||
-rw-r--r-- | Kernel/Console.h | 38 | ||||
-rw-r--r-- | Kernel/Keyboard.cpp | 31 | ||||
-rw-r--r-- | Kernel/Keyboard.h | 11 | ||||
-rw-r--r-- | Kernel/Makefile | 6 | ||||
-rw-r--r-- | Kernel/TTY.cpp | 25 | ||||
-rw-r--r-- | Kernel/TTY.h | 16 | ||||
-rw-r--r-- | Kernel/Task.cpp | 15 | ||||
-rw-r--r-- | Kernel/Task.h | 5 | ||||
-rw-r--r-- | Kernel/VGA.cpp | 14 | ||||
-rw-r--r-- | Kernel/VGA.h | 1 | ||||
-rw-r--r-- | Kernel/VirtualConsole.cpp | 367 | ||||
-rw-r--r-- | Kernel/VirtualConsole.h | 61 | ||||
-rw-r--r-- | Kernel/init.cpp | 27 | ||||
-rw-r--r-- | VirtualFileSystem/CharacterDevice.cpp | 5 | ||||
-rw-r--r-- | VirtualFileSystem/CharacterDevice.h | 12 | ||||
-rw-r--r-- | VirtualFileSystem/FullDevice.cpp | 1 | ||||
-rw-r--r-- | VirtualFileSystem/NullDevice.cpp | 1 | ||||
-rw-r--r-- | VirtualFileSystem/RandomDevice.cpp | 1 | ||||
-rw-r--r-- | VirtualFileSystem/VirtualFileSystem.cpp | 10 | ||||
-rw-r--r-- | VirtualFileSystem/VirtualFileSystem.h | 7 | ||||
-rw-r--r-- | VirtualFileSystem/ZeroDevice.cpp | 1 |
22 files changed, 611 insertions, 344 deletions
diff --git a/Kernel/Console.cpp b/Kernel/Console.cpp index 9bb4f0cb14..2ad66f2f4e 100644 --- a/Kernel/Console.cpp +++ b/Kernel/Console.cpp @@ -2,7 +2,6 @@ #include "VGA.h" #include "IO.h" #include "kprintf.h" -#include <AK/String.h> // Bytes output to 0xE9 end up on the Bochs console. It's very handy. #define CONSOLE_OUT_TO_E9 @@ -15,6 +14,7 @@ Console& Console::the() } Console::Console() + : CharacterDevice(5, 1) { s_the = this; } @@ -35,218 +35,15 @@ ssize_t Console::read(byte*, size_t) return 0; } -inline bool isParameter(byte ch) -{ - return ch >= 0x30 && ch <= 0x3f; -} - -inline bool isIntermediate(byte ch) -{ - return ch >= 0x20 && ch <= 0x2f; -} - -inline bool isFinal(byte ch) -{ - return ch >= 0x40 && ch <= 0x7e; -} - -unsigned parseUInt(const String& str, bool& ok) -{ - unsigned value = 0; - for (size_t i = 0; i < str.length(); ++i) { - if (str[i] < '0' || str[i] > '9') { - ok = false; - return 0; - } - value = value * 10; - value += str[i] - '0'; - } - ok = true; - return value; -} - -enum class VGAColor : byte { - Black = 0, - Blue, - Green, - Cyan, - Red, - Magenta, - Brown, - LightGray, - DarkGray, - BrightBlue, - BrightGreen, - BrightCyan, - BrightRed, - BrightMagenta, - Yellow, - White, -}; - -enum class ANSIColor : byte { - Black = 0, - Red, - Green, - Brown, - Blue, - Magenta, - Cyan, - LightGray, - DarkGray, - BrightRed, - BrightGreen, - Yellow, - BrightBlue, - BrightMagenta, - BrightCyan, - White, -}; - -static inline VGAColor ansiColorToVGA(ANSIColor color) -{ - switch (color) { - case ANSIColor::Black: return VGAColor::Black; - case ANSIColor::Red: return VGAColor::Red; - case ANSIColor::Brown: return VGAColor::Brown; - case ANSIColor::Blue: return VGAColor::Blue; - case ANSIColor::Magenta: return VGAColor::Magenta; - case ANSIColor::Green: return VGAColor::Green; - case ANSIColor::Cyan: return VGAColor::Cyan; - case ANSIColor::LightGray: return VGAColor::LightGray; - case ANSIColor::DarkGray: return VGAColor::DarkGray; - case ANSIColor::BrightRed: return VGAColor::BrightRed; - case ANSIColor::BrightGreen: return VGAColor::BrightGreen; - case ANSIColor::Yellow: return VGAColor::Yellow; - case ANSIColor::BrightBlue: return VGAColor::BrightBlue; - case ANSIColor::BrightMagenta: return VGAColor::BrightMagenta; - case ANSIColor::BrightCyan: return VGAColor::BrightCyan; - case ANSIColor::White: return VGAColor::White; - } - ASSERT_NOT_REACHED(); - return VGAColor::LightGray; -} - -static inline byte ansiColorToVGA(byte color) -{ - return (byte)ansiColorToVGA((ANSIColor)color); -} - -void Console::escape$m(const Vector<unsigned>& params) -{ - for (auto param : params) { - switch (param) { - case 0: - // Reset - m_currentAttribute = 0x07; - break; - case 1: - // Bold - m_currentAttribute |= 8; - break; - case 30: - case 31: - case 32: - case 33: - case 34: - case 35: - case 36: - case 37: - // Foreground color - m_currentAttribute &= ~0x7; - m_currentAttribute |= ansiColorToVGA(param - 30); - break; - case 40: - case 41: - case 42: - case 43: - case 44: - case 45: - case 46: - case 47: - // Background color - m_currentAttribute &= ~0x70; - m_currentAttribute |= ansiColorToVGA(param - 30) << 8; - break; - } - } - vga_set_attr(m_currentAttribute); -} - -void Console::escape$s(const Vector<unsigned>&) -{ - m_savedCursorRow = m_cursorRow; - m_savedCursorColumn = m_cursorColumn; -} - -void Console::escape$u(const Vector<unsigned>&) -{ - m_cursorRow = m_savedCursorRow; - m_cursorColumn = m_savedCursorColumn; - vga_set_cursor(m_cursorRow, m_cursorColumn); -} - -void Console::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]; - m_cursorRow = row - 1; - m_cursorColumn = col - 1; - vga_set_cursor(row - 1, col - 1); -} - -void Console::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. - notImplemented(); - break; - case 1: - // FIXME: Clear from cursor to beginning of screen. - notImplemented(); - break; - case 2: - vga_clear(); - break; - case 3: - // FIXME: <esc>[3J should also clear the scrollback buffer. - vga_clear(); - break; - } -} - -void Console::executeEscapeSequence(byte final) +ssize_t Console::write(const byte* data, size_t size) { - auto paramparts = String((const char*)m_parameters.data(), m_parameters.size()).split(';'); - Vector<unsigned> params; - for (auto& parampart : paramparts) { - bool ok; - unsigned value = parseUInt(parampart, ok); - if (!ok) { - // FIXME: Should we do something else? - return; - } - params.append(value); - } - switch (final) { - 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(); + if (!size) + return 0; + if (!m_implementation) + return 0; + for (size_t i = 0; i < size; ++i) + putChar(data[i]); + return 0; } void Console::putChar(char ch) @@ -255,83 +52,10 @@ void Console::putChar(char ch) //if (ch != 27) IO::out8(0xe9, ch); #endif - auto scrollup = [&] { - if (m_cursorRow == (m_rows - 1)) { - vga_scroll_up(); - } else { - ++m_cursorRow; - } - m_cursorColumn = 0; - }; - - switch (m_escState) { - case ExpectBracket: - if (ch == '[') - m_escState = ExpectParameter; - else - m_escState = Normal; - return; - case ExpectParameter: - if (isParameter(ch)) { - m_parameters.append(ch); - return; - } - m_escState = ExpectIntermediate; - // fall through - case ExpectIntermediate: - if (isIntermediate(ch)) { - m_intermediates.append(ch); - return; - } - m_escState = ExpectFinal; - // fall through - case ExpectFinal: - if (isFinal(ch)) { - m_escState = Normal; - executeEscapeSequence(ch); - return; - } - m_escState = Normal; - return; - case Normal: - break; - } - - switch (ch) { - case '\0': - return; - case '\033': - m_escState = ExpectBracket; - return; - case 8: // Backspace - if (m_cursorColumn) { - --m_cursorColumn;\ - vga_set_cursor(m_cursorRow, m_cursorColumn); - vga_putch_at(m_cursorRow, m_cursorColumn, ' '); - return; - } - break; - case '\n': - scrollup(); - vga_set_cursor(m_cursorRow, m_cursorColumn); - return; - } - - vga_putch_at(m_cursorRow, m_cursorColumn, ch); - - ++m_cursorColumn; - if (m_cursorColumn >= m_columns) - scrollup(); - vga_set_cursor(m_cursorRow, m_cursorColumn); + if (m_implementation) + m_implementation->onConsoleReceive(ch); } -ssize_t Console::write(const byte* data, size_t size) +ConsoleImplementation::~ConsoleImplementation() { - if (!size) - return 0; - - for (size_t i = 0; i < size; ++i) - putChar(data[i]); - return 0; } - diff --git a/Kernel/Console.h b/Kernel/Console.h index b10882d2f1..c870b086d5 100644 --- a/Kernel/Console.h +++ b/Kernel/Console.h @@ -4,6 +4,12 @@ #include <AK/Vector.h> #include <VirtualFileSystem/CharacterDevice.h> +class ConsoleImplementation { +public: + virtual ~ConsoleImplementation(); + virtual void onConsoleReceive(byte) = 0; +}; + class Console final : public CharacterDevice { public: static Console& the() PURE; @@ -15,37 +21,11 @@ public: virtual ssize_t read(byte* buffer, size_t size) override; virtual ssize_t write(const byte* data, size_t size) override; + void setImplementation(ConsoleImplementation* implementation) { m_implementation = implementation; } + void putChar(char); private: - 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>&); - - const byte m_rows { 25 }; - const byte m_columns { 80 }; - - byte m_cursorRow { 0 }; - byte m_cursorColumn { 0 }; - - byte m_savedCursorRow { 0 }; - byte m_savedCursorColumn { 0 }; - - byte m_currentAttribute { 0x07 }; - - void executeEscapeSequence(byte final); - - enum EscapeState { - Normal, - ExpectBracket, - ExpectParameter, - ExpectIntermediate, - ExpectFinal, - }; - EscapeState m_escState { Normal }; - Vector<byte> m_parameters; - Vector<byte> m_intermediates; + ConsoleImplementation* m_implementation { nullptr }; }; diff --git a/Kernel/Keyboard.cpp b/Kernel/Keyboard.cpp index cd41f2723c..9a34c2f88c 100644 --- a/Kernel/Keyboard.cpp +++ b/Kernel/Keyboard.cpp @@ -4,6 +4,7 @@ #include "VGA.h" #include "PIC.h" #include "Keyboard.h" +#include "VirtualConsole.h" #include <AK/Assertions.h> #define IRQ_KEYBOARD 1 @@ -56,12 +57,31 @@ void Keyboard::handleIRQ() // key has been depressed break; } - if (!m_modifiers) + if (m_modifiers & MOD_ALT) { + switch (map[ch]) { + case '1': + case '2': + case '3': + VirtualConsole::switchTo(map[ch] - '0' - 1); + break; + default: + break; + } + } + if (!m_modifiers) { + if (m_client) + m_client->onKeyPress(map[ch]); m_queue.enqueue(map[ch]); - else if (m_modifiers & MOD_SHIFT) + } else if (m_modifiers & MOD_SHIFT) { + if (m_client) + m_client->onKeyPress(shift_map[ch]); m_queue.enqueue(shift_map[ch]); - else if (m_modifiers & MOD_CTRL) { + } else if (m_modifiers & MOD_CTRL) { // FIXME: This is obviously not a good enough way to process ctrl+whatever. + if (m_client) { + m_client->onKeyPress('^'); + m_client->onKeyPress(shift_map[ch]); + } m_queue.enqueue('^'); m_queue.enqueue(shift_map[ch]); } @@ -72,6 +92,7 @@ void Keyboard::handleIRQ() Keyboard::Keyboard() : IRQHandler(IRQ_KEYBOARD) + , CharacterDevice(85, 1) { // Empty the buffer of any pending data. // I don't care what you've been pressing until now! @@ -106,3 +127,7 @@ ssize_t Keyboard::write(const byte* data, size_t size) { return 0; } + +KeyboardClient::~KeyboardClient() +{ +} diff --git a/Kernel/Keyboard.h b/Kernel/Keyboard.h index d5554c77ac..21334cf714 100644 --- a/Kernel/Keyboard.h +++ b/Kernel/Keyboard.h @@ -6,11 +6,21 @@ #include <VirtualFileSystem/CharacterDevice.h> #include "IRQHandler.h" +class KeyboardClient { +public: + virtual ~KeyboardClient(); + virtual void onKeyPress(byte) = 0; +}; + class Keyboard final : public IRQHandler, public CharacterDevice { public: + static Keyboard& the() PURE; + virtual ~Keyboard() override; Keyboard(); + void setClient(KeyboardClient*); + private: // ^IRQHandler virtual void handleIRQ() override; @@ -20,6 +30,7 @@ private: virtual ssize_t write(const byte* buffer, size_t) override; virtual bool hasDataAvailableForRead() const override; + KeyboardClient* m_client { nullptr }; CircularQueue<byte, 16> m_queue; byte m_modifiers { 0 }; }; diff --git a/Kernel/Makefile b/Kernel/Makefile index b391a3f2a9..25574ecd16 100644 --- a/Kernel/Makefile +++ b/Kernel/Makefile @@ -19,7 +19,9 @@ KERNEL_OBJS = \ IRQHandler.o \ kprintf.o \ ProcFileSystem.o \ - RTC.o + RTC.o \ + TTY.o \ + VirtualConsole.o VFS_OBJS = \ ../VirtualFileSystem/DiskDevice.o \ @@ -53,7 +55,7 @@ KERNEL = kernel BOOTLOADER = Boot/boot.bin IMAGE = .floppy-image ARCH_FLAGS = -STANDARD_FLAGS = -std=c++17 -nostdinc++ -nostdlib +STANDARD_FLAGS = -std=c++17 -nostdinc++ -nostdlib #-nostdinc KERNEL_FLAGS = -ffreestanding -fno-stack-protector -fno-ident WARNING_FLAGS = -Wextra -Wall -Wundef -Wcast-qual -Wwrite-strings FLAVOR_FLAGS = -mregparm=3 -march=i386 -m32 -fno-exceptions -fno-rtti -fmerge-all-constants -fno-unroll-loops -fno-pie -fno-pic diff --git a/Kernel/TTY.cpp b/Kernel/TTY.cpp new file mode 100644 index 0000000000..e3196a0015 --- /dev/null +++ b/Kernel/TTY.cpp @@ -0,0 +1,25 @@ +#include "TTY.h" + +TTY::TTY(unsigned major, unsigned minor) + : CharacterDevice(major, minor) +{ +} + +TTY::~TTY() +{ +} + +ssize_t TTY::read(byte* buffer, size_t size) +{ + return 0; +} + +ssize_t TTY::write(const byte* buffer, size_t size) +{ + return 0; +} + +bool TTY::hasDataAvailableForRead() const +{ + return false; +} diff --git a/Kernel/TTY.h b/Kernel/TTY.h new file mode 100644 index 0000000000..fe42643b37 --- /dev/null +++ b/Kernel/TTY.h @@ -0,0 +1,16 @@ +#pragma once + +#include <VirtualFileSystem/CharacterDevice.h> + +class TTY : public CharacterDevice { +public: + virtual ~TTY() override; + + virtual ssize_t read(byte*, size_t) override; + virtual ssize_t write(const byte*, size_t) override; + virtual bool hasDataAvailableForRead() const override; + +protected: + TTY(unsigned major, unsigned minor); +}; + diff --git a/Kernel/Task.cpp b/Kernel/Task.cpp index 9e1de47fea..63bbfcb1f5 100644 --- a/Kernel/Task.cpp +++ b/Kernel/Task.cpp @@ -363,7 +363,7 @@ Task* Task::createKernelTask(void (*e)(), String&& name) return task; } -Task::Task(String&& name, uid_t uid, gid_t gid, pid_t parentPID, RingLevel ring, RetainPtr<VirtualFileSystem::Node>&& cwd, RetainPtr<VirtualFileSystem::Node>&& executable) +Task::Task(String&& name, uid_t uid, gid_t gid, pid_t parentPID, RingLevel ring, RetainPtr<VirtualFileSystem::Node>&& cwd, RetainPtr<VirtualFileSystem::Node>&& executable, TTY* tty) : m_name(move(name)) , m_pid(next_pid++) , m_uid(uid) @@ -373,10 +373,17 @@ Task::Task(String&& name, uid_t uid, gid_t gid, pid_t parentPID, RingLevel ring, , m_cwd(move(cwd)) , m_executable(move(executable)) , m_parentPID(parentPID) + , m_tty(tty) { - m_fileHandles.append(nullptr); // stdin - m_fileHandles.append(nullptr); // stdout - m_fileHandles.append(nullptr); // stderr + if (tty) { + m_fileHandles.append(tty->open(O_RDONLY)); // stdin + m_fileHandles.append(tty->open(O_WRONLY)); // stdout + m_fileHandles.append(tty->open(O_WRONLY)); // stderr + } else { + m_fileHandles.append(nullptr); // stdin + m_fileHandles.append(nullptr); // stdout + m_fileHandles.append(nullptr); // stderr + } m_nextRegion = LinearAddress(0x600000); diff --git a/Kernel/Task.h b/Kernel/Task.h index 8b0cfe8a1e..73cc504a79 100644 --- a/Kernel/Task.h +++ b/Kernel/Task.h @@ -7,6 +7,7 @@ #include <AK/Vector.h> #include "i386.h" #include <VirtualFileSystem/VirtualFileSystem.h> +#include "TTY.h" //#define TASK_SANITY_CHECKS @@ -141,7 +142,7 @@ private: friend class MemoryManager; friend bool scheduleNewTask(); - Task(String&& name, uid_t, gid_t, pid_t parentPID, RingLevel, RetainPtr<VirtualFileSystem::Node>&& cwd = nullptr, RetainPtr<VirtualFileSystem::Node>&& executable = nullptr); + Task(String&& name, uid_t, gid_t, pid_t parentPID, RingLevel, RetainPtr<VirtualFileSystem::Node>&& cwd = nullptr, RetainPtr<VirtualFileSystem::Node>&& executable = nullptr, TTY* = nullptr); void allocateLDT(); @@ -175,6 +176,8 @@ private: RetainPtr<VirtualFileSystem::Node> m_cwd; RetainPtr<VirtualFileSystem::Node> m_executable; + TTY* m_tty { nullptr }; + struct Region : public Retainable<Region> { Region(LinearAddress, size_t, RetainPtr<Zone>&&, String&&); ~Region(); diff --git a/Kernel/VGA.cpp b/Kernel/VGA.cpp index b2571ecdd8..ebb39a5c4a 100644 --- a/Kernel/VGA.cpp +++ b/Kernel/VGA.cpp @@ -12,13 +12,23 @@ void vga_scroll_up() { InterruptDisabler disabler; memcpy(vga_mem, vga_mem + 160, 160 * 24); - memset(vga_mem + (160 * 24), 0, 160); + vga_clear_row(24); +} + +void vga_clear_row(word line) +{ + InterruptDisabler disabler; + word* linemem = (word*)&vga_mem[line * 160]; + for (word i = 0; i < 80; ++i) { + linemem[i] = 0x0720; + } } void vga_clear() { InterruptDisabler disabler; - memset(vga_mem, 0, 160 * 25); + for (word i = 0; i < 25; ++i) + vga_clear_row(i); } void vga_putch_at(byte row, byte column, byte ch) diff --git a/Kernel/VGA.h b/Kernel/VGA.h index a1e505f53c..2c34affe3f 100644 --- a/Kernel/VGA.h +++ b/Kernel/VGA.h @@ -11,3 +11,4 @@ WORD vga_get_cursor(); void vga_putch_at(byte row, byte column, byte ch); void vga_scroll_up(); void vga_clear(); +void vga_clear_row(word); diff --git a/Kernel/VirtualConsole.cpp b/Kernel/VirtualConsole.cpp new file mode 100644 index 0000000000..8d9e4a8ae3 --- /dev/null +++ b/Kernel/VirtualConsole.cpp @@ -0,0 +1,367 @@ +#include "VirtualConsole.h" +#include "VGA.h" +#include "kmalloc.h" +#include "i386.h" +#include "StdLib.h" +#include <AK/String.h> + +static byte* s_vgaBuffer = (byte*)0xb8000; +static VirtualConsole* s_consoles[6]; +static unsigned s_activeConsole; + +void VirtualConsole::initialize() +{ + memset(s_consoles, 0, sizeof(s_consoles)); + s_activeConsole = 0; +} + +VirtualConsole::VirtualConsole(unsigned index, InitialContents initialContents) + : TTY(4, index) + , m_index(index) +{ + s_consoles[index] = this; + kprintf("VirtualConsole %u @ %p\n", index, this); + m_buffer = (byte*)kmalloc(80 * 25 * 2); + if (initialContents == AdoptCurrentVGABuffer) { + memcpy(m_buffer, s_vgaBuffer, 80 * 25 * 2); + auto vgaCursor = vga_get_cursor(); + m_cursorRow = vgaCursor / 80; + m_cursorColumn = vgaCursor % 80; + } else { + word* linemem = (word*)m_buffer; + for (word i = 0; i < 80 * 25; ++i) + linemem[i] = 0x0720; + } +} + +VirtualConsole::~VirtualConsole() +{ +} + +void VirtualConsole::switchTo(unsigned index) +{ + if (index == s_activeConsole) + return; + dbgprintf("[VC] Switch to %u\n", index); + ASSERT(index < 6); + ASSERT(s_consoles[index]); + InterruptDisabler disabler; + s_consoles[s_activeConsole]->setActive(false); + s_activeConsole = index; + s_consoles[s_activeConsole]->setActive(true); +} + +void VirtualConsole::setActive(bool b) +{ + if (b == m_active) + return; + + InterruptDisabler disabler; + + m_active = b; + if (!m_active) { + dbgprintf("%u becomes inactive, copying to buffer\n", m_index); + memcpy(m_buffer, s_vgaBuffer, 80 * 25 * 2); + return; + } + + dbgprintf("%u becomes active, copying from buffer, set cursor %u,%u\n", m_index, m_cursorRow, m_cursorColumn); + memcpy(s_vgaBuffer, m_buffer, 80 * 25 * 2); + vga_set_cursor(m_cursorRow, m_cursorColumn); +} + +inline bool isParameter(byte ch) +{ + return ch >= 0x30 && ch <= 0x3f; +} + +inline bool isIntermediate(byte ch) +{ + return ch >= 0x20 && ch <= 0x2f; +} + +inline bool isFinal(byte ch) +{ + return ch >= 0x40 && ch <= 0x7e; +} + +unsigned parseUInt(const String& str, bool& ok) +{ + unsigned value = 0; + for (size_t i = 0; i < str.length(); ++i) { + if (str[i] < '0' || str[i] > '9') { + ok = false; + return 0; + } + value = value * 10; + value += str[i] - '0'; + } + ok = true; + return value; +} + +enum class VGAColor : byte { + Black = 0, + Blue, + Green, + Cyan, + Red, + Magenta, + Brown, + LightGray, + DarkGray, + BrightBlue, + BrightGreen, + BrightCyan, + BrightRed, + BrightMagenta, + Yellow, + White, +}; + +enum class ANSIColor : byte { + Black = 0, + Red, + Green, + Brown, + Blue, + Magenta, + Cyan, + LightGray, + DarkGray, + BrightRed, + BrightGreen, + Yellow, + BrightBlue, + BrightMagenta, + BrightCyan, + White, +}; + +static inline VGAColor ansiColorToVGA(ANSIColor color) +{ + switch (color) { + case ANSIColor::Black: return VGAColor::Black; + case ANSIColor::Red: return VGAColor::Red; + case ANSIColor::Brown: return VGAColor::Brown; + case ANSIColor::Blue: return VGAColor::Blue; + case ANSIColor::Magenta: return VGAColor::Magenta; + case ANSIColor::Green: return VGAColor::Green; + case ANSIColor::Cyan: return VGAColor::Cyan; + case ANSIColor::LightGray: return VGAColor::LightGray; + case ANSIColor::DarkGray: return VGAColor::DarkGray; + case ANSIColor::BrightRed: return VGAColor::BrightRed; + case ANSIColor::BrightGreen: return VGAColor::BrightGreen; + case ANSIColor::Yellow: return VGAColor::Yellow; + case ANSIColor::BrightBlue: return VGAColor::BrightBlue; + case ANSIColor::BrightMagenta: return VGAColor::BrightMagenta; + case ANSIColor::BrightCyan: return VGAColor::BrightCyan; + case ANSIColor::White: return VGAColor::White; + } + ASSERT_NOT_REACHED(); + return VGAColor::LightGray; +} + +static inline byte ansiColorToVGA(byte color) +{ + return (byte)ansiColorToVGA((ANSIColor)color); +} + +void VirtualConsole::escape$m(const Vector<unsigned>& params) +{ + for (auto param : params) { + switch (param) { + case 0: + // Reset + m_currentAttribute = 0x07; + break; + case 1: + // Bold + m_currentAttribute |= 8; + break; + case 30: + case 31: + case 32: + case 33: + case 34: + case 35: + case 36: + case 37: + // Foreground color + m_currentAttribute &= ~0x7; + m_currentAttribute |= ansiColorToVGA(param - 30); + break; + case 40: + case 41: + case 42: + case 43: + case 44: + case 45: + case 46: + case 47: + // Background color + m_currentAttribute &= ~0x70; + m_currentAttribute |= ansiColorToVGA(param - 30) << 8; + break; + } + } + vga_set_attr(m_currentAttribute); +} + +void VirtualConsole::escape$s(const Vector<unsigned>&) +{ + m_savedCursorRow = m_cursorRow; + m_savedCursorColumn = m_cursorColumn; +} + +void VirtualConsole::escape$u(const Vector<unsigned>&) +{ + m_cursorRow = m_savedCursorRow; + m_cursorColumn = m_savedCursorColumn; + vga_set_cursor(m_cursorRow, m_cursorColumn); +} + +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]; + m_cursorRow = row - 1; + m_cursorColumn = col - 1; + vga_set_cursor(row - 1, col - 1); +} + +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. + notImplemented(); + break; + case 1: + // FIXME: Clear from cursor to beginning of screen. + notImplemented(); + break; + case 2: + vga_clear(); + break; + case 3: + // FIXME: <esc>[3J should also clear the scrollback buffer. + vga_clear(); + break; + } +} + +void VirtualConsole::executeEscapeSequence(byte final) +{ + auto paramparts = String((const char*)m_parameters.data(), m_parameters.size()).split(';'); + Vector<unsigned> params; + for (auto& parampart : paramparts) { + bool ok; + unsigned value = parseUInt(parampart, ok); + if (!ok) { + // FIXME: Should we do something else? + return; + } + params.append(value); + } + switch (final) { + 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(); +} + +void VirtualConsole::onChar(byte ch) +{ + auto scrollup = [&] { + if (m_cursorRow == (m_rows - 1)) { + vga_scroll_up(); + } else { + ++m_cursorRow; + } + m_cursorColumn = 0; + }; + + switch (m_escState) { + case ExpectBracket: + if (ch == '[') + m_escState = ExpectParameter; + else + m_escState = Normal; + return; + case ExpectParameter: + if (isParameter(ch)) { + m_parameters.append(ch); + return; + } + m_escState = ExpectIntermediate; + // fall through + case ExpectIntermediate: + if (isIntermediate(ch)) { + m_intermediates.append(ch); + return; + } + m_escState = ExpectFinal; + // fall through + case ExpectFinal: + if (isFinal(ch)) { + m_escState = Normal; + executeEscapeSequence(ch); + return; + } + m_escState = Normal; + return; + case Normal: + break; + } + + switch (ch) { + case '\0': + return; + case '\033': + m_escState = ExpectBracket; + return; + case 8: // Backspace + if (m_cursorColumn) { + --m_cursorColumn;\ + vga_set_cursor(m_cursorRow, m_cursorColumn); + vga_putch_at(m_cursorRow, m_cursorColumn, ' '); + return; + } + break; + case '\n': + scrollup(); + vga_set_cursor(m_cursorRow, m_cursorColumn); + return; + } + + vga_putch_at(m_cursorRow, m_cursorColumn, ch); + + ++m_cursorColumn; + if (m_cursorColumn >= m_columns) + scrollup(); + vga_set_cursor(m_cursorRow, m_cursorColumn); +} + +void VirtualConsole::onKeyPress(byte ch) +{ + onChar(ch); +} + +void VirtualConsole::onConsoleReceive(byte ch) +{ + onChar(ch); +} diff --git a/Kernel/VirtualConsole.h b/Kernel/VirtualConsole.h new file mode 100644 index 0000000000..203bb99241 --- /dev/null +++ b/Kernel/VirtualConsole.h @@ -0,0 +1,61 @@ +#pragma once + +#include "TTY.h" +#include "Keyboard.h" +#include "Console.h" + +class VirtualConsole final : public TTY, public KeyboardClient, public ConsoleImplementation { +public: + enum InitialContents { Cleared, AdoptCurrentVGABuffer }; + + VirtualConsole(unsigned index, InitialContents = Cleared); + virtual ~VirtualConsole() override; + + void adoptCurrentVGABuffer(); + void setActive(bool); + + static void switchTo(unsigned); + static void initialize(); + +private: + // ^KeyboardClient + void onKeyPress(byte) override; + + // ^ConsoleImplementation + void onConsoleReceive(byte) override; + + void onChar(byte); + + byte* m_buffer; + unsigned m_index; + bool m_active { false }; + + 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>&); + + const byte m_rows { 25 }; + const byte m_columns { 80 }; + byte m_cursorRow { 0 }; + byte m_cursorColumn { 0 }; + byte m_savedCursorRow { 0 }; + byte m_savedCursorColumn { 0 }; + byte m_currentAttribute { 0x07 }; + + void executeEscapeSequence(byte final); + + enum EscapeState { + Normal, + ExpectBracket, + ExpectParameter, + ExpectIntermediate, + ExpectFinal, + }; + EscapeState m_escState { Normal }; + Vector<byte> m_parameters; + Vector<byte> m_intermediates; + +}; + diff --git a/Kernel/init.cpp b/Kernel/init.cpp index 5a70233312..59d2e83444 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -25,6 +25,7 @@ #include "Console.h" #include "ProcFileSystem.h" #include "RTC.h" +#include "VirtualConsole.h" #define TEST_VFS #define KSYMS @@ -33,6 +34,10 @@ system_t system; +VirtualConsole* tty0; +VirtualConsole* tty1; +VirtualConsole* tty2; + void banner() { kprintf("\n\033[33;1mWelcome to \033[36;1mSerenity OS!\033[0m\n\n"); @@ -110,18 +115,22 @@ static void init_stage2() auto vfs = make<VirtualFileSystem>(); auto dev_zero = make<ZeroDevice>(); - vfs->registerCharacterDevice(1, 5, *dev_zero); + vfs->registerCharacterDevice(*dev_zero); auto dev_null = make<NullDevice>(); - vfs->registerCharacterDevice(1, 3, *dev_null); + vfs->registerCharacterDevice(*dev_null); auto dev_full = make<FullDevice>(); - vfs->registerCharacterDevice(1, 7, *dev_full); + vfs->registerCharacterDevice(*dev_full); auto dev_random = make<RandomDevice>(); - vfs->registerCharacterDevice(1, 8, *dev_random); + vfs->registerCharacterDevice(*dev_random); + + vfs->registerCharacterDevice(*keyboard); - vfs->registerCharacterDevice(85, 1, *keyboard); + vfs->registerCharacterDevice(*tty0); + vfs->registerCharacterDevice(*tty1); + vfs->registerCharacterDevice(*tty2); auto dev_hd0 = IDEDiskDevice::create(); auto e2fs = Ext2FileSystem::create(dev_hd0.copyRef()); @@ -207,7 +216,15 @@ void init() kmalloc_init(); vga_init(); + VirtualConsole::initialize(); + tty0 = new VirtualConsole(0, VirtualConsole::AdoptCurrentVGABuffer); + tty1 = new VirtualConsole(1); + tty2 = new VirtualConsole(2); + tty0->setActive(true); + tty1->setActive(false); + tty2->setActive(false); auto console = make<Console>(); + console->setImplementation(tty0); RTC::initialize(); PIC::initialize(); diff --git a/VirtualFileSystem/CharacterDevice.cpp b/VirtualFileSystem/CharacterDevice.cpp index dc743ff91f..81405925e9 100644 --- a/VirtualFileSystem/CharacterDevice.cpp +++ b/VirtualFileSystem/CharacterDevice.cpp @@ -4,4 +4,7 @@ CharacterDevice::~CharacterDevice() { } - +OwnPtr<FileHandle> CharacterDevice::open(int options) +{ + //VirtualFileSystem::the().open() +} diff --git a/VirtualFileSystem/CharacterDevice.h b/VirtualFileSystem/CharacterDevice.h index 6d017a1eed..c67ea471a1 100644 --- a/VirtualFileSystem/CharacterDevice.h +++ b/VirtualFileSystem/CharacterDevice.h @@ -2,16 +2,26 @@ #include <AK/Types.h> #include "Limits.h" +#include "FileHandle.h" class CharacterDevice { public: virtual ~CharacterDevice(); + virtual OwnPtr<FileHandle> open(int options); + virtual bool hasDataAvailableForRead() const = 0; virtual Unix::ssize_t read(byte* buffer, Unix::size_t bufferSize) = 0; virtual Unix::ssize_t write(const byte* buffer, Unix::size_t bufferSize) = 0; + unsigned major() const { return m_major; } + unsigned minor() const { return m_minor; } + protected: - CharacterDevice() { } + CharacterDevice(unsigned major, unsigned minor) : m_major(major), m_minor(minor) { } + +private: + unsigned m_major { 0 }; + unsigned m_minor{ 0 }; }; diff --git a/VirtualFileSystem/FullDevice.cpp b/VirtualFileSystem/FullDevice.cpp index 065cbfaea4..0a74e21c02 100644 --- a/VirtualFileSystem/FullDevice.cpp +++ b/VirtualFileSystem/FullDevice.cpp @@ -5,6 +5,7 @@ #include <AK/kstdio.h> FullDevice::FullDevice() + : CharacterDevice(1, 7) { } diff --git a/VirtualFileSystem/NullDevice.cpp b/VirtualFileSystem/NullDevice.cpp index e958e12366..85a3171097 100644 --- a/VirtualFileSystem/NullDevice.cpp +++ b/VirtualFileSystem/NullDevice.cpp @@ -4,6 +4,7 @@ #include <AK/kstdio.h> NullDevice::NullDevice() + : CharacterDevice(1, 3) { } diff --git a/VirtualFileSystem/RandomDevice.cpp b/VirtualFileSystem/RandomDevice.cpp index d34ac8400b..2f5cb50807 100644 --- a/VirtualFileSystem/RandomDevice.cpp +++ b/VirtualFileSystem/RandomDevice.cpp @@ -3,6 +3,7 @@ #include <AK/StdLib.h> RandomDevice::RandomDevice() + : CharacterDevice(1, 8) { } diff --git a/VirtualFileSystem/VirtualFileSystem.cpp b/VirtualFileSystem/VirtualFileSystem.cpp index 23c2ef4ced..c69628cffc 100644 --- a/VirtualFileSystem/VirtualFileSystem.cpp +++ b/VirtualFileSystem/VirtualFileSystem.cpp @@ -5,15 +5,11 @@ #include <AK/kmalloc.h> #include <AK/kstdio.h> #include <AK/ktime.h> +#include "CharacterDevice.h" #include "sys-errno.h" //#define VFS_DEBUG -static dword encodedDevice(unsigned major, unsigned minor) -{ - return (minor & 0xff) | (major << 8) | ((minor & ~0xff) << 12); -} - static VirtualFileSystem* s_the; VirtualFileSystem& VirtualFileSystem::the() @@ -542,9 +538,9 @@ VirtualFileSystem::Mount::Mount(InodeIdentifier host, RetainPtr<FileSystem>&& gu { } -void VirtualFileSystem::registerCharacterDevice(unsigned major, unsigned minor, CharacterDevice& device) +void VirtualFileSystem::registerCharacterDevice(CharacterDevice& device) { - m_characterDevices.set(encodedDevice(major, minor), &device); + m_characterDevices.set(encodedDevice(device.major(), device.minor()), &device); } void VirtualFileSystem::forEachMount(Function<void(const Mount&)> callback) const diff --git a/VirtualFileSystem/VirtualFileSystem.h b/VirtualFileSystem/VirtualFileSystem.h index 183fba5a1c..e2860c669a 100644 --- a/VirtualFileSystem/VirtualFileSystem.h +++ b/VirtualFileSystem/VirtualFileSystem.h @@ -22,6 +22,11 @@ class CharacterDevice; class FileHandle; +inline constexpr dword encodedDevice(unsigned major, unsigned minor) +{ + return (minor & 0xff) | (major << 8) | ((minor & ~0xff) << 12); +} + class VirtualFileSystem { public: static void initializeGlobals(); @@ -94,7 +99,7 @@ public: bool touch(const String&path); - void registerCharacterDevice(unsigned major, unsigned minor, CharacterDevice&); + void registerCharacterDevice(CharacterDevice&); size_t mountCount() const { return m_mounts.size(); } void forEachMount(Function<void(const Mount&)>) const; diff --git a/VirtualFileSystem/ZeroDevice.cpp b/VirtualFileSystem/ZeroDevice.cpp index 2b08c85110..5ee17059a8 100644 --- a/VirtualFileSystem/ZeroDevice.cpp +++ b/VirtualFileSystem/ZeroDevice.cpp @@ -4,6 +4,7 @@ #include <AK/kstdio.h> ZeroDevice::ZeroDevice() + : CharacterDevice(1, 5) { } |