Kernel: Add partial support for shell

We don't handle arrow keys, and the tty does not know how to clear
the screeen :D
This commit is contained in:
Bananymous 2023-04-05 01:30:58 +03:00
parent db076058b9
commit a423cd8bb3
5 changed files with 46 additions and 175 deletions

View File

@ -19,10 +19,9 @@ namespace Kernel
void rerender_buffer() const;
BAN::Vector<BAN::String> parse_arguments(BAN::StringView) const;
BAN::ErrorOr<void> process_command(const BAN::Vector<BAN::String>&);
void key_event_callback(Input::KeyEvent);
BAN::ErrorOr<void> update_prompt();
private:
private:
BAN::Vector<BAN::String> m_old_buffer;
BAN::Vector<BAN::String> m_buffer;
BAN::String m_prompt_string;

View File

@ -39,6 +39,7 @@ namespace Kernel
void render_from_buffer(uint32_t x, uint32_t y);
void on_key(Input::KeyEvent);
void do_backspace();
private:
enum class State

View File

@ -11,11 +11,23 @@
#include <kernel/RTC.h>
#include <kernel/Shell.h>
#include <fcntl.h>
#include <ctype.h>
#include <fcntl.h>
#include <unistd.h>
#define TTY_PRINT(...) \
do { \
BAN::String message_ = BAN::String::formatted(__VA_ARGS__); \
MUST(Process::current()->write(STDOUT_FILENO, message_.data(), message_.size())); \
} while (false)
#define TTY_PRINTLN(...) \
do { \
TTY_PRINT(__VA_ARGS__); \
MUST(Process::current()->write(STDOUT_FILENO, "\n", 1)); \
} while (false)
#define TTY_PRINT(...)
#define TTY_PRINTLN(...)
namespace Kernel
{
@ -99,17 +111,22 @@ namespace Kernel
void Shell::run()
{
int fd = MUST(Process::current()->open("/dev/tty1"sv, O_RDONLY));
TTY_PRINT("{}", m_prompt);
BAN::String input;
for (;;)
{
uint8_t buffer[128];
size_t n_read = MUST(Process::current()->read(fd, buffer, sizeof(buffer)));
dprintln("{}", BAN::StringView((const char*)buffer, n_read));
//Input::KeyEvent event;
//MUST(Process::current()->read(fd, &event, sizeof(event)));
//key_event_callback(event);
char buffer[128];
size_t n_read = MUST(Process::current()->read(STDIN_FILENO, buffer, sizeof(buffer)));
MUST(input.append(BAN::StringView(buffer, n_read)));
if (input.back() == '\n')
{
input.pop_back();
if (auto res = process_command(parse_arguments(input)); res.is_error())
TTY_PRINTLN("{}", res.error());
TTY_PRINT("{}", m_prompt);
input.clear();
}
}
}
@ -540,147 +557,4 @@ argument_done:
TTY_PRINT("\e[{}G{}\e[K", m_prompt_length + 1, m_buffer[m_cursor_pos.line]);
}
static uint32_t get_last_length(BAN::StringView sv)
{
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 BAN::Math::min<uint32_t>(sv.size(), 1);
}
static uint32_t get_next_length(BAN::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 BAN::Math::min<uint32_t>(sv.size(), 1);
}
static uint32_t get_unicode_character_count(BAN::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::key_event_callback(Input::KeyEvent event)
{
if (event.released())
return;
BAN::String& current_buffer = m_buffer[m_cursor_pos.line];
switch (event.key)
{
case Input::Key::Backspace:
if (m_cursor_pos.col > 0)
{
TTY_PRINT("\e[D{} ", current_buffer.sv().substring(m_cursor_pos.index));
uint32_t len = get_last_length(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_PRINTLN("");
auto arguments = parse_arguments(current_buffer.sv());
if (!arguments.empty())
{
if (auto res = process_command(arguments); res.is_error())
TTY_PRINTLN("{}", res.error());
MUST(m_old_buffer.push_back(current_buffer));
m_buffer = m_old_buffer;
MUST(m_buffer.push_back(""sv));
m_cursor_pos.line = m_buffer.size() - 1;
}
m_cursor_pos.col = 0;
m_cursor_pos.index = 0;
TTY_PRINT("{}", m_prompt);
break;
}
case Input::Key::Escape:
TTY_PRINTLN("time since boot {} ms", PIT::ms_since_boot());
break;
case Input::Key::Tab:
break;
case Input::Key::ArrowLeft:
if (m_cursor_pos.index > 0)
{
uint32_t len = get_last_length(current_buffer.sv().substring(0, m_cursor_pos.index));
m_cursor_pos.index -= len;
m_cursor_pos.col--;
}
break;
case Input::Key::ArrowRight:
if (m_cursor_pos.index < current_buffer.size())
{
uint32_t len = get_next_length(current_buffer.sv().substring(m_cursor_pos.index));
m_cursor_pos.index += len;
m_cursor_pos.col++;
}
break;
case Input::Key::ArrowUp:
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 = get_unicode_character_count(new_buffer);
rerender_buffer();
}
break;
case Input::Key::ArrowDown:
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 = get_unicode_character_count(new_buffer);
rerender_buffer();
}
break;
case Input::Key::A:
if (event.ctrl())
{
m_cursor_pos.col = m_cursor_pos.index = 0;
break;
}
// fall through
default:
{
const char* utf8 = Input::key_event_to_utf8(event);
if (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) % m_tty->width() + 1);
}
}

View File

@ -92,6 +92,7 @@ namespace Kernel
ansi = "\n";
break;
case Input::Key::Backspace:
ansi = nullptr;
if (m_output.bytes > 0)
{
// Multibyte UTF8
@ -103,19 +104,20 @@ namespace Kernel
ASSERT(m_output.bytes > 0);
m_output.bytes--;
}
ansi = "\b \b";
do_backspace();
}
// Control sequence
else if (m_output.bytes >= 2 && m_output.buffer[m_output.bytes - 2] == '\e')
{
m_output.bytes -= 2;
ansi = "\b\b \b\b";
do_backspace();
do_backspace();
}
// Ascii
else
{
m_output.bytes--;
ansi = "\b \b";
do_backspace();
}
}
break;
@ -156,6 +158,16 @@ namespace Kernel
m_terminal_driver->clear(m_background);
}
void TTY::do_backspace()
{
if (m_column > 0)
{
m_column--;
putchar_at(' ', m_column, m_row);
set_cursor_position(m_column, m_row);
}
}
void TTY::set_cursor_position(uint32_t x, uint32_t y)
{
static uint32_t last_x = -1;

View File

@ -222,25 +222,10 @@ static void init2(void* terminal_driver)
ASSERT(tty1);
DeviceManager::get().add_device(tty1);
MUST(Process::create_kernel(
[](void*)
{
MUST(Process::current()->init_stdio());
while (true)
{
char buffer[1024];
int n_read = MUST(Process::current()->read(STDIN_FILENO, buffer, sizeof(buffer)));
MUST(Process::current()->write(STDOUT_FILENO, buffer, n_read));
dprintln("{} bytes", n_read);
}
}, nullptr
));
return;
MUST(Process::create_kernel(
[](void*)
{
MUST(Process::current()->init_stdio());
Shell* shell = new Shell();
ASSERT(shell);
shell->run();