forked from Bananymous/banan-os
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:
parent
db076058b9
commit
a423cd8bb3
|
@ -19,10 +19,9 @@ namespace Kernel
|
||||||
void rerender_buffer() const;
|
void rerender_buffer() const;
|
||||||
BAN::Vector<BAN::String> parse_arguments(BAN::StringView) const;
|
BAN::Vector<BAN::String> parse_arguments(BAN::StringView) const;
|
||||||
BAN::ErrorOr<void> process_command(const BAN::Vector<BAN::String>&);
|
BAN::ErrorOr<void> process_command(const BAN::Vector<BAN::String>&);
|
||||||
void key_event_callback(Input::KeyEvent);
|
|
||||||
BAN::ErrorOr<void> update_prompt();
|
BAN::ErrorOr<void> update_prompt();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BAN::Vector<BAN::String> m_old_buffer;
|
BAN::Vector<BAN::String> m_old_buffer;
|
||||||
BAN::Vector<BAN::String> m_buffer;
|
BAN::Vector<BAN::String> m_buffer;
|
||||||
BAN::String m_prompt_string;
|
BAN::String m_prompt_string;
|
||||||
|
|
|
@ -39,6 +39,7 @@ namespace Kernel
|
||||||
void render_from_buffer(uint32_t x, uint32_t y);
|
void render_from_buffer(uint32_t x, uint32_t y);
|
||||||
|
|
||||||
void on_key(Input::KeyEvent);
|
void on_key(Input::KeyEvent);
|
||||||
|
void do_backspace();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class State
|
enum class State
|
||||||
|
|
|
@ -11,11 +11,23 @@
|
||||||
#include <kernel/RTC.h>
|
#include <kernel/RTC.h>
|
||||||
#include <kernel/Shell.h>
|
#include <kernel/Shell.h>
|
||||||
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <ctype.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
|
namespace Kernel
|
||||||
{
|
{
|
||||||
|
@ -99,17 +111,22 @@ namespace Kernel
|
||||||
|
|
||||||
void Shell::run()
|
void Shell::run()
|
||||||
{
|
{
|
||||||
int fd = MUST(Process::current()->open("/dev/tty1"sv, O_RDONLY));
|
|
||||||
|
|
||||||
TTY_PRINT("{}", m_prompt);
|
TTY_PRINT("{}", m_prompt);
|
||||||
|
BAN::String input;
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
uint8_t buffer[128];
|
char buffer[128];
|
||||||
size_t n_read = MUST(Process::current()->read(fd, buffer, sizeof(buffer)));
|
size_t n_read = MUST(Process::current()->read(STDIN_FILENO, buffer, sizeof(buffer)));
|
||||||
dprintln("{}", BAN::StringView((const char*)buffer, n_read));
|
|
||||||
//Input::KeyEvent event;
|
MUST(input.append(BAN::StringView(buffer, n_read)));
|
||||||
//MUST(Process::current()->read(fd, &event, sizeof(event)));
|
if (input.back() == '\n')
|
||||||
//key_event_callback(event);
|
{
|
||||||
|
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]);
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -92,6 +92,7 @@ namespace Kernel
|
||||||
ansi = "\n";
|
ansi = "\n";
|
||||||
break;
|
break;
|
||||||
case Input::Key::Backspace:
|
case Input::Key::Backspace:
|
||||||
|
ansi = nullptr;
|
||||||
if (m_output.bytes > 0)
|
if (m_output.bytes > 0)
|
||||||
{
|
{
|
||||||
// Multibyte UTF8
|
// Multibyte UTF8
|
||||||
|
@ -103,19 +104,20 @@ namespace Kernel
|
||||||
ASSERT(m_output.bytes > 0);
|
ASSERT(m_output.bytes > 0);
|
||||||
m_output.bytes--;
|
m_output.bytes--;
|
||||||
}
|
}
|
||||||
ansi = "\b \b";
|
do_backspace();
|
||||||
}
|
}
|
||||||
// Control sequence
|
// Control sequence
|
||||||
else if (m_output.bytes >= 2 && m_output.buffer[m_output.bytes - 2] == '\e')
|
else if (m_output.bytes >= 2 && m_output.buffer[m_output.bytes - 2] == '\e')
|
||||||
{
|
{
|
||||||
m_output.bytes -= 2;
|
m_output.bytes -= 2;
|
||||||
ansi = "\b\b \b\b";
|
do_backspace();
|
||||||
|
do_backspace();
|
||||||
}
|
}
|
||||||
// Ascii
|
// Ascii
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_output.bytes--;
|
m_output.bytes--;
|
||||||
ansi = "\b \b";
|
do_backspace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -156,6 +158,16 @@ namespace Kernel
|
||||||
m_terminal_driver->clear(m_background);
|
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)
|
void TTY::set_cursor_position(uint32_t x, uint32_t y)
|
||||||
{
|
{
|
||||||
static uint32_t last_x = -1;
|
static uint32_t last_x = -1;
|
||||||
|
|
|
@ -222,25 +222,10 @@ static void init2(void* terminal_driver)
|
||||||
ASSERT(tty1);
|
ASSERT(tty1);
|
||||||
DeviceManager::get().add_device(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(
|
MUST(Process::create_kernel(
|
||||||
[](void*)
|
[](void*)
|
||||||
{
|
{
|
||||||
|
MUST(Process::current()->init_stdio());
|
||||||
Shell* shell = new Shell();
|
Shell* shell = new Shell();
|
||||||
ASSERT(shell);
|
ASSERT(shell);
|
||||||
shell->run();
|
shell->run();
|
||||||
|
|
Loading…
Reference in New Issue