Kernel: Add support for arrow keys in Shell

This commit is contained in:
Bananymous 2023-01-13 15:07:24 +02:00
parent bf4b26d1fd
commit f547a788f2
4 changed files with 144 additions and 47 deletions

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <BAN/String.h> #include <BAN/String.h>
#include <BAN/Vector.h>
#include <kernel/Input.h> #include <kernel/Input.h>
#include <kernel/TTY.h> #include <kernel/TTY.h>
@ -15,19 +16,30 @@ namespace Kernel
static Shell& Get(); static Shell& Get();
void SetTTY(TTY* tty); void SetTTY(TTY* tty);
void SetPrompt(BAN::StringView);
void Run(); void Run();
private: private:
Shell(); Shell();
void PrintPrompt(); void ReRenderBuffer() const;
void ProcessCommand(const BAN::Vector<BAN::StringView>&); void ProcessCommand(const BAN::Vector<BAN::StringView>&);
void KeyEventCallback(Input::KeyEvent); void KeyEventCallback(Input::KeyEvent);
void MouseMoveEventCallback(Input::MouseMoveEvent); void MouseMoveEventCallback(Input::MouseMoveEvent);
private: private:
TTY* m_tty; TTY* m_tty;
BAN::String m_buffer; BAN::Vector<BAN::String> m_old_buffer;
BAN::Vector<BAN::String> m_buffer;
BAN::String m_prompt;
uint32_t m_prompt_length = 0;
struct
{
uint32_t line = 0;
uint32_t col = 0;
uint32_t index = 0;
} m_cursor_pos;
struct struct
{ {

View File

@ -50,12 +50,32 @@ namespace Kernel
{ {
Input::register_key_event_callback([](Input::KeyEvent event) { Shell::Get().KeyEventCallback(event); }); Input::register_key_event_callback([](Input::KeyEvent event) { Shell::Get().KeyEventCallback(event); });
Input::register_mouse_move_event_callback([](Input::MouseMoveEvent event) { Shell::Get().MouseMoveEventCallback(event); }); Input::register_mouse_move_event_callback([](Input::MouseMoveEvent event) { Shell::Get().MouseMoveEventCallback(event); });
m_buffer.Reserve(128); SetPrompt("\\[\e[32m\\]user\\[\e[m\\]# "_sv);
MUST(m_buffer.PushBack(""_sv));
} }
void Shell::PrintPrompt() void Shell::SetPrompt(StringView prompt)
{ {
TTY_PRINT("\e[32muser\e[m# "); m_prompt_length = 0;
m_prompt = String();
bool skipping = false;
for (size_t i = 0; i < prompt.Size(); i++)
{
if (i < prompt.Size() - 1 && prompt[i] == '\\')
{
if (prompt[i + 1] == '[')
skipping = true;
if (prompt[i + 1] == ']')
skipping = false;
i++;
continue;
}
MUST(m_prompt.PushBack(prompt[i]));
if (!skipping)
m_prompt_length++;
}
} }
void Shell::SetTTY(TTY* tty) void Shell::SetTTY(TTY* tty)
@ -65,7 +85,8 @@ namespace Kernel
void Shell::Run() void Shell::Run()
{ {
PrintPrompt(); ASSERT(m_tty);
TTY_PRINT("{}", m_prompt);
for (;;) for (;;)
{ {
asm volatile("hlt"); asm volatile("hlt");
@ -184,35 +205,39 @@ namespace Kernel
} }
static bool IsSingleUnicode(StringView sv) void Shell::ReRenderBuffer() const
{ {
if (sv.Size() == 2 && ((uint8_t)sv[0] >> 5) != 0b110) TTY_PRINT("\e[{}G{}\e[K", m_prompt_length + 1, m_buffer[m_cursor_pos.line]);
return false;
if (sv.Size() == 3 && ((uint8_t)sv[0] >> 4) != 0b1110)
return false;
if (sv.Size() == 4 && ((uint8_t)sv[0] >> 3) != 0b11110)
return false;
for (uint32_t i = 1; i < sv.Size(); i++)
if (((uint8_t)sv[i] >> 6) != 0b10)
return false;
return true;
} }
static uint32_t GetLastLength(StringView sv) static uint32_t GetLastLength(StringView sv)
{ {
if (sv.Size() < 2) if (sv.Size() >= 2 && ((uint8_t)sv[sv.Size() - 2] >> 5) == 0b110) return 2;
return sv.Size(); if (sv.Size() >= 3 && ((uint8_t)sv[sv.Size() - 3] >> 4) == 0b1110) return 3;
if (sv.Size() >= 4 && ((uint8_t)sv[sv.Size() - 4] >> 3) == 0b11110) return 4;
for (uint32_t len = 2; len <= 4; len++) return Math::min<uint32_t>(sv.Size(), 1);
{
if (sv.Size() < len)
return 1;
if (IsSingleUnicode(sv.Substring(sv.Size() - len)))
return len;
} }
return 1; static uint32_t GetNextLength(StringView sv)
{
if (sv.Size() >= 2 && ((uint8_t)sv[0] >> 5) == 0b110) return 2;
if (sv.Size() >= 3 && ((uint8_t)sv[0] >> 4) == 0b1110) return 3;
if (sv.Size() >= 4 && ((uint8_t)sv[0] >> 3) == 0b11110) return 4;
return Math::min<uint32_t>(sv.Size(), 1);
}
static uint32_t GetUnicodeCharacterCount(StringView sv)
{
uint32_t len = 0;
for (uint32_t i = 0; i < sv.Size(); i++)
{
uint8_t ch = sv[i];
if ((ch >> 5) == 0b110) i += 1;
if ((ch >> 4) == 0b1110) i += 2;
if ((ch >> 3) == 0b11110) i += 3;
len++;
}
return len;
} }
void Shell::KeyEventCallback(Input::KeyEvent event) void Shell::KeyEventCallback(Input::KeyEvent event)
@ -220,28 +245,38 @@ namespace Kernel
if (!event.pressed) if (!event.pressed)
return; return;
String& current_buffer = m_buffer[m_cursor_pos.line];
switch (event.key) switch (event.key)
{ {
case Input::Key::Backspace: case Input::Key::Backspace:
if (m_cursor_pos.col > 0)
{ {
if (!m_buffer.Empty()) TTY_PRINT("\e[D{} ", current_buffer.SV().Substring(m_cursor_pos.index));
{
TTY_PRINT("\b \b", 3);
uint32_t last_len = GetLastLength(m_buffer); uint32_t len = GetLastLength(current_buffer.SV().Substring(0, m_cursor_pos.index));
for (uint32_t i = 0; i < last_len; i++) m_cursor_pos.index -= len;
m_buffer.PopBack(); current_buffer.Erase(m_cursor_pos.index, len);
m_cursor_pos.col--;
} }
break; break;
}
case Input::Key::Enter: case Input::Key::Enter:
case Input::Key::NumpadEnter: case Input::Key::NumpadEnter:
{ {
TTY_PRINT("\n"); TTY_PRINTLN("");
ProcessCommand(MUST(m_buffer.SV().Split(' '))); auto arguments = MUST(current_buffer.SV().Split(' '));
m_buffer.Clear(); if (!arguments.Empty())
PrintPrompt(); {
ProcessCommand(arguments);
MUST(m_old_buffer.PushBack(current_buffer));
m_buffer = m_old_buffer;
MUST(m_buffer.PushBack(""_sv));
m_cursor_pos.line = m_buffer.Size() - 1;
}
m_cursor_pos.col = 0;
m_cursor_pos.index = 0;
TTY_PRINT("{}", m_prompt);
break; break;
} }
@ -252,18 +287,62 @@ namespace Kernel
case Input::Key::Tab: case Input::Key::Tab:
break; break;
case Input::Key::Left:
if (m_cursor_pos.index > 0)
{
uint32_t len = GetLastLength(current_buffer.SV().Substring(0, m_cursor_pos.index));
m_cursor_pos.index -= len;
m_cursor_pos.col--;
}
break;
case Input::Key::Right:
if (m_cursor_pos.index < current_buffer.Size())
{
uint32_t len = GetNextLength(current_buffer.SV().Substring(m_cursor_pos.index));
m_cursor_pos.index += len;
m_cursor_pos.col++;
}
break;
case Input::Key::Up:
if (m_cursor_pos.line > 0)
{
const auto& new_buffer = m_buffer[m_cursor_pos.line - 1];
m_cursor_pos.line--;
m_cursor_pos.index = new_buffer.Size();
m_cursor_pos.col = GetUnicodeCharacterCount(new_buffer);
ReRenderBuffer();
}
break;
case Input::Key::Down:
if (m_cursor_pos.line < m_buffer.Size() - 1)
{
const auto& new_buffer = m_buffer[m_cursor_pos.line + 1];
m_cursor_pos.line++;
m_cursor_pos.index = new_buffer.Size();
m_cursor_pos.col = GetUnicodeCharacterCount(new_buffer);
ReRenderBuffer();
}
break;
default: default:
{ {
const char* utf8 = Input::key_event_to_utf8(event); const char* utf8 = Input::key_event_to_utf8(event);
if (utf8) if (utf8)
{ {
TTY_PRINT("{}", utf8); TTY_PRINT("{}{}", utf8, current_buffer.SV().Substring(m_cursor_pos.index));
m_buffer.Append(utf8); MUST(current_buffer.Insert(utf8, m_cursor_pos.index));
m_cursor_pos.index += strlen(utf8);
m_cursor_pos.col++;
} }
break; break;
} }
} }
TTY_PRINT("\e[{}G", m_prompt_length + m_cursor_pos.col + 1);
if (m_mouse_pos.exists) if (m_mouse_pos.exists)
VESA::PutBitmapAt(s_pointer, m_mouse_pos.x, m_mouse_pos.y, VESA::Color::BRIGHT_WHITE); VESA::PutBitmapAt(s_pointer, m_mouse_pos.x, m_mouse_pos.y, VESA::Color::BRIGHT_WHITE);
} }

View File

@ -2,5 +2,7 @@
void foo() void foo()
{ {
strlen(""); strlen(nullptr);
strncpy(nullptr, nullptr, 0);
memcpy(nullptr, nullptr, 0);
} }

View File

@ -63,29 +63,33 @@ extern "C" void kernel_main()
dprintln("Invalid multiboot magic number"); dprintln("Invalid multiboot magic number");
return; return;
} }
dprintln("Serial output initialized");
auto cmdline = ParseCommandLine(); auto cmdline = ParseCommandLine();
kmalloc_initialize(); kmalloc_initialize();
dprintln("kmalloc initialized"); dprintln("kmalloc initialized");
MMU::Intialize();
dprintln("MMU initialized");
APIC::Initialize(cmdline.force_pic);
dprintln("APIX initialized");
gdt_initialize(); gdt_initialize();
dprintln("GDT initialized"); dprintln("GDT initialized");
IDT::initialize(); IDT::initialize();
dprintln("IDT initialized"); dprintln("IDT initialized");
MMU::Intialize();
dprintln("MMU initialized");
if (!VESA::Initialize()) if (!VESA::Initialize())
return; return;
dprintln("VESA initialized");
TTY* tty1 = new TTY; TTY* tty1 = new TTY;
APIC::Initialize(cmdline.force_pic);
dprintln("APIC initialized");
PIT::initialize(); PIT::initialize();
dprintln("PIT initialized");
if (!Input::initialize()) if (!Input::initialize())
return; return;
dprintln("8042 initialized");
ENABLE_INTERRUPTS(); ENABLE_INTERRUPTS();