diff --git a/kernel/include/kernel/Shell.h b/kernel/include/kernel/Shell.h index b9a02b311..42827dd51 100644 --- a/kernel/include/kernel/Shell.h +++ b/kernel/include/kernel/Shell.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -15,19 +16,30 @@ namespace Kernel static Shell& Get(); void SetTTY(TTY* tty); + void SetPrompt(BAN::StringView); void Run(); private: Shell(); - void PrintPrompt(); + void ReRenderBuffer() const; void ProcessCommand(const BAN::Vector&); void KeyEventCallback(Input::KeyEvent); void MouseMoveEventCallback(Input::MouseMoveEvent); private: - TTY* m_tty; - BAN::String m_buffer; + TTY* m_tty; + BAN::Vector m_old_buffer; + BAN::Vector 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 { diff --git a/kernel/kernel/Shell.cpp b/kernel/kernel/Shell.cpp index 28418f27e..3826f6ea3 100644 --- a/kernel/kernel/Shell.cpp +++ b/kernel/kernel/Shell.cpp @@ -50,12 +50,32 @@ namespace Kernel { 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); }); - 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) @@ -65,7 +85,8 @@ namespace Kernel void Shell::Run() { - PrintPrompt(); + ASSERT(m_tty); + TTY_PRINT("{}", m_prompt); for (;;) { 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) - 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; + TTY_PRINT("\e[{}G{}\e[K", m_prompt_length + 1, m_buffer[m_cursor_pos.line]); } static uint32_t GetLastLength(StringView sv) { - if (sv.Size() < 2) - return sv.Size(); + if (sv.Size() >= 2 && ((uint8_t)sv[sv.Size() - 2] >> 5) == 0b110) return 2; + 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(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(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) - return 1; - - if (IsSingleUnicode(sv.Substring(sv.Size() - len))) - return len; + 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 1; + return len; } void Shell::KeyEventCallback(Input::KeyEvent event) @@ -220,28 +245,38 @@ namespace Kernel if (!event.pressed) return; + String& current_buffer = m_buffer[m_cursor_pos.line]; + switch (event.key) { case Input::Key::Backspace: - { - if (!m_buffer.Empty()) + if (m_cursor_pos.col > 0) { - TTY_PRINT("\b \b", 3); + TTY_PRINT("\e[D{} ", current_buffer.SV().Substring(m_cursor_pos.index)); - uint32_t last_len = GetLastLength(m_buffer); - for (uint32_t i = 0; i < last_len; i++) - m_buffer.PopBack(); + uint32_t len = GetLastLength(current_buffer.SV().Substring(0, m_cursor_pos.index)); + m_cursor_pos.index -= len; + current_buffer.Erase(m_cursor_pos.index, len); + m_cursor_pos.col--; } break; - } case Input::Key::Enter: case Input::Key::NumpadEnter: { - TTY_PRINT("\n"); - ProcessCommand(MUST(m_buffer.SV().Split(' '))); - m_buffer.Clear(); - PrintPrompt(); + TTY_PRINTLN(""); + auto arguments = MUST(current_buffer.SV().Split(' ')); + if (!arguments.Empty()) + { + 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; } @@ -252,18 +287,62 @@ namespace Kernel case Input::Key::Tab: 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: { const char* utf8 = Input::key_event_to_utf8(event); if (utf8) { - TTY_PRINT("{}", utf8); - m_buffer.Append(utf8); + TTY_PRINT("{}{}", utf8, current_buffer.SV().Substring(m_cursor_pos.index)); + MUST(current_buffer.Insert(utf8, m_cursor_pos.index)); + m_cursor_pos.index += strlen(utf8); + m_cursor_pos.col++; } break; } } + TTY_PRINT("\e[{}G", m_prompt_length + m_cursor_pos.col + 1); + if (m_mouse_pos.exists) VESA::PutBitmapAt(s_pointer, m_mouse_pos.x, m_mouse_pos.y, VESA::Color::BRIGHT_WHITE); } diff --git a/kernel/kernel/build_libc.cpp b/kernel/kernel/build_libc.cpp index 116ea6804..e3739d902 100644 --- a/kernel/kernel/build_libc.cpp +++ b/kernel/kernel/build_libc.cpp @@ -2,5 +2,7 @@ void foo() { - strlen(""); + strlen(nullptr); + strncpy(nullptr, nullptr, 0); + memcpy(nullptr, nullptr, 0); } \ No newline at end of file diff --git a/kernel/kernel/kernel.cpp b/kernel/kernel/kernel.cpp index bf0b5c4ea..b55363b4c 100644 --- a/kernel/kernel/kernel.cpp +++ b/kernel/kernel/kernel.cpp @@ -63,29 +63,33 @@ extern "C" void kernel_main() dprintln("Invalid multiboot magic number"); return; } + dprintln("Serial output initialized"); auto cmdline = ParseCommandLine(); kmalloc_initialize(); dprintln("kmalloc initialized"); - MMU::Intialize(); - dprintln("MMU initialized"); - - APIC::Initialize(cmdline.force_pic); - dprintln("APIX initialized"); gdt_initialize(); dprintln("GDT initialized"); IDT::initialize(); dprintln("IDT initialized"); + MMU::Intialize(); + dprintln("MMU initialized"); + if (!VESA::Initialize()) return; + dprintln("VESA initialized"); TTY* tty1 = new TTY; + APIC::Initialize(cmdline.force_pic); + dprintln("APIC initialized"); PIT::initialize(); + dprintln("PIT initialized"); if (!Input::initialize()) return; + dprintln("8042 initialized"); ENABLE_INTERRUPTS();