summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Kling <awesomekling@gmail.com>2018-10-30 13:59:29 +0100
committerAndreas Kling <awesomekling@gmail.com>2018-10-30 13:59:29 +0100
commit68739dc43e6bc42f9cac79fe5cbec714ddeeb218 (patch)
tree705579b6a757d6219d28e6a8c2a4244eb828bcaa
parentbd2b5327d042f5bf04ae7089d8b36c113313f8f3 (diff)
downloadserenity-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.cpp300
-rw-r--r--Kernel/Console.h38
-rw-r--r--Kernel/Keyboard.cpp31
-rw-r--r--Kernel/Keyboard.h11
-rw-r--r--Kernel/Makefile6
-rw-r--r--Kernel/TTY.cpp25
-rw-r--r--Kernel/TTY.h16
-rw-r--r--Kernel/Task.cpp15
-rw-r--r--Kernel/Task.h5
-rw-r--r--Kernel/VGA.cpp14
-rw-r--r--Kernel/VGA.h1
-rw-r--r--Kernel/VirtualConsole.cpp367
-rw-r--r--Kernel/VirtualConsole.h61
-rw-r--r--Kernel/init.cpp27
-rw-r--r--VirtualFileSystem/CharacterDevice.cpp5
-rw-r--r--VirtualFileSystem/CharacterDevice.h12
-rw-r--r--VirtualFileSystem/FullDevice.cpp1
-rw-r--r--VirtualFileSystem/NullDevice.cpp1
-rw-r--r--VirtualFileSystem/RandomDevice.cpp1
-rw-r--r--VirtualFileSystem/VirtualFileSystem.cpp10
-rw-r--r--VirtualFileSystem/VirtualFileSystem.h7
-rw-r--r--VirtualFileSystem/ZeroDevice.cpp1
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)
{
}