Kernel: Add support for arrow keys in Shell
This commit is contained in:
parent
bf4b26d1fd
commit
f547a788f2
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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;
|
||||||
|
return Math::min<uint32_t>(sv.Size(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
for (uint32_t len = 2; len <= 4; len++)
|
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++)
|
||||||
{
|
{
|
||||||
if (sv.Size() < len)
|
uint8_t ch = sv[i];
|
||||||
return 1;
|
if ((ch >> 5) == 0b110) i += 1;
|
||||||
|
if ((ch >> 4) == 0b1110) i += 2;
|
||||||
if (IsSingleUnicode(sv.Substring(sv.Size() - len)))
|
if ((ch >> 3) == 0b11110) i += 3;
|
||||||
return len;
|
len++;
|
||||||
}
|
}
|
||||||
|
return len;
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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("\b \b", 3);
|
TTY_PRINT("\e[D{} ", current_buffer.SV().Substring(m_cursor_pos.index));
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,5 +2,7 @@
|
||||||
|
|
||||||
void foo()
|
void foo()
|
||||||
{
|
{
|
||||||
strlen("");
|
strlen(nullptr);
|
||||||
|
strncpy(nullptr, nullptr, 0);
|
||||||
|
memcpy(nullptr, nullptr, 0);
|
||||||
}
|
}
|
|
@ -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();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue