kernel+terminal: Move pseudoterminal input processing to kernel-space
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
apio 2023-09-22 22:45:35 +02:00
parent 441e04076a
commit ffdcc843eb
Signed by: apio
GPG Key ID: B8A7D06E42258954
7 changed files with 162 additions and 134 deletions

View File

@ -5,6 +5,7 @@
#include "fs/devpts/FileSystem.h"
#include "memory/MemoryManager.h"
#include "thread/Scheduler.h"
#include <luna/CType.h>
Result<SharedPtr<VFS::Inode>> MasterPTY::create_pair(int index)
{
@ -57,6 +58,130 @@ Result<void> MasterPTY::handle_background_process_group(bool can_succeed, int si
return err(EIO);
}
bool MasterPTY::is_canonical()
{
return m_settings.c_lflag & ICANON;
}
Result<void> MasterPTY::handle_input(u8 key)
{
bool is_special_character { false };
if (is_canonical())
{
if (key == m_settings.c_cc[VERASE])
{
auto maybe_char = m_current_line_buffer.try_pop();
if (maybe_char.has_value() && maybe_char.value())
{
if ((m_settings.c_lflag & ECHO) && (m_settings.c_lflag & ECHOE))
{
u8 backspace = (u8)'\b';
m_buffer.append_data(&backspace, 1);
if (_iscntrl(maybe_char.value())) m_buffer.append_data(&backspace, 1);
if (maybe_char.value() == '\t')
{
m_buffer.append_data(&backspace, 1);
m_buffer.append_data(&backspace, 1);
}
return {};
}
return {};
}
if ((m_settings.c_lflag & ECHOE)) return {};
else
is_special_character = true;
}
if (key == m_settings.c_cc[VEOF])
{
const auto size = m_current_line_buffer.size();
auto buffer = Buffer { m_current_line_buffer.release_data(), size };
TRY(m_lines.try_append(move(buffer)));
is_special_character = true;
}
if (m_settings.c_lflag & ISIG)
{
if (key == m_settings.c_cc[VINTR])
{
if (!(m_settings.c_lflag & NOFLSH)) m_current_line_buffer.clear();
if (m_foreground_process_group.has_value())
Scheduler::for_each_in_process_group(*m_foreground_process_group, [](Thread* thread) {
thread->send_signal(SIGINT);
return true;
});
is_special_character = true;
}
if (key == m_settings.c_cc[VQUIT])
{
if (!(m_settings.c_lflag & NOFLSH)) m_current_line_buffer.clear();
if (m_foreground_process_group.has_value())
Scheduler::for_each_in_process_group(*m_foreground_process_group, [](Thread* thread) {
thread->send_signal(SIGQUIT);
return true;
});
is_special_character = true;
}
}
}
if (!is_special_character)
{
if (is_canonical())
{
const usize max_line_size = key == '\n' ? 4096 : 4095;
if (m_current_line_buffer.size() < max_line_size) TRY(m_current_line_buffer.try_append(key));
if (key == '\n')
{
const auto size = m_current_line_buffer.size();
auto buffer = Buffer { m_current_line_buffer.release_data(), size };
TRY(m_lines.try_append(move(buffer)));
}
}
else
TRY(m_slave->m_buffer.append_data(&key, 1));
}
if (!(m_settings.c_lflag & ECHO)) return {};
if (_iscntrl(key))
{
if (m_settings.c_lflag & ECHOCTL)
{
bool should_echo = true;
if (key == '\n' || key == '\t' || key == '\0' || key == m_settings.c_cc[VEOF]) should_echo = false;
if (should_echo)
{
u8 caret_notation[2] = {
'^',
'\0',
};
if (key == 0x7f) caret_notation[1] = '?';
else
caret_notation[1] = key + 0x40;
m_buffer.append_data(caret_notation, 2);
}
else
m_buffer.append_data(&key, 1);
}
}
else
m_buffer.append_data(&key, 1);
return {};
}
Result<usize> MasterPTY::read(u8* buf, usize, usize length) const
{
length = m_buffer.dequeue_data(buf, length);
@ -66,7 +191,7 @@ Result<usize> MasterPTY::read(u8* buf, usize, usize length) const
Result<usize> MasterPTY::write(const u8* buf, usize, usize length)
{
TRY(m_slave->m_buffer.append_data(buf, length));
for (usize i = 0; i < length; i++) { TRY(handle_input(buf[i])); }
return length;
}

View File

@ -67,7 +67,15 @@ class MasterPTY : public VFS::DeviceInode
mutable Option<pid_t> m_foreground_process_group;
struct winsize m_window;
typedef Vector<u8> Line;
Vector<Buffer> m_lines;
Line m_current_line_buffer;
Result<void> handle_background_process_group(bool can_succeed, int signo) const;
Result<void> handle_input(u8 key);
bool is_canonical();
int m_index;

View File

@ -10,7 +10,15 @@ Result<usize> SlavePTY::read(u8* buf, usize, usize length) const
TRY(m_master->handle_background_process_group(false, SIGTTIN));
length = m_buffer.dequeue_data(buf, length);
if (!m_master->is_canonical()) length = m_buffer.dequeue_data(buf, length);
else
{
if (m_master->m_lines.size() == 0) { return 0; }
auto& line = m_master->m_lines[0];
length = line.dequeue_data(buf, length);
if (line.size() == 0) m_master->m_lines.remove_at(0);
}
return length;
}
@ -26,6 +34,13 @@ Result<usize> SlavePTY::write(const u8* buf, usize, usize length)
return length;
}
bool SlavePTY::will_block_if_read() const
{
if (!m_master->is_canonical()) return m_buffer.is_empty();
else
return !m_master->m_lines.size();
}
Result<u64> SlavePTY::ioctl(int request, void* arg)
{
auto* current = Scheduler::current();

View File

@ -42,10 +42,7 @@ class SlavePTY : public VFS::DeviceInode
return err(EINVAL);
}
bool will_block_if_read() const override
{
return m_buffer.is_empty();
}
bool will_block_if_read() const override;
void did_link() override
{

View File

@ -20,6 +20,16 @@ class Buffer
Buffer();
~Buffer();
/**
* @brief Create a new Buffer object, adopting existing data.
*
* The data provided will be owned by the buffer.
*
* @param data The data to use.
* @param size The amount of bytes in the data.
*/
Buffer(u8* data, usize size);
Buffer(Buffer&& other);
Buffer(const Buffer& other) = delete; // For now.
@ -161,8 +171,6 @@ class Buffer
}
private:
Buffer(u8* data, usize size);
u8* m_data { nullptr };
usize m_size { 0 };
};

View File

@ -95,115 +95,7 @@ Result<ui::EventResult> TerminalWidget::handle_key_event(const ui::KeyEventReque
// invalid zero byte into the terminal input (this would also happen on Shift or Ctrl keypresses).
if (request.letter == '\0') return ui::EventResult::DidNotHandle;
return handle_input(request.letter);
}
Result<ui::EventResult> TerminalWidget::handle_input(char key)
{
query_termios();
bool is_special_character { false };
if (is_canonical())
{
if (key == m_settings.c_cc[VERASE])
{
auto maybe_char = m_line_buffer.try_pop();
if (maybe_char.has_value() && maybe_char.value())
{
if ((m_settings.c_lflag & ECHO) && (m_settings.c_lflag & ECHOE))
{
put_code_point(L'\b');
if (_iscntrl(maybe_char.value())) put_code_point(L'\b');
if (maybe_char.value() == '\t')
{
put_code_point(L'\b');
put_code_point(L'\b');
}
return ui::EventResult::DidHandle;
}
return ui::EventResult::DidNotHandle;
}
if ((m_settings.c_lflag & ECHOE)) return ui::EventResult::DidNotHandle;
else
is_special_character = true;
}
if (key == m_settings.c_cc[VEOF])
{
write(m_pty, m_line_buffer.data(), m_line_buffer.size());
m_line_buffer.clear();
// FIXME: tell the kernel that process may read without blocking.
is_special_character = true;
}
if (m_settings.c_lflag & ISIG)
{
if (key == m_settings.c_cc[VINTR])
{
if (!(m_settings.c_lflag & NOFLSH)) m_line_buffer.clear();
pid_t group = tcgetpgrp(m_pty);
TRY(os::Process::kill(-group, SIGINT));
is_special_character = true;
}
if (key == m_settings.c_cc[VQUIT])
{
if (!(m_settings.c_lflag & NOFLSH)) m_line_buffer.clear();
pid_t group = tcgetpgrp(m_pty);
TRY(os::Process::kill(-group, SIGQUIT));
is_special_character = true;
}
}
}
if (!is_special_character)
{
if (is_canonical())
{
m_line_buffer.try_append((u8)key);
if (key == '\n')
{
write(m_pty, m_line_buffer.data(), m_line_buffer.size());
m_line_buffer.clear();
}
}
else
write(m_pty, &key, 1);
}
if (!(m_settings.c_lflag & ECHO)) return ui::EventResult::DidNotHandle;
if (_iscntrl(key))
{
if (m_settings.c_lflag & ECHOCTL)
{
bool should_echo = true;
if (key == '\n' || key == '\t' || key == '\0' || key == m_settings.c_cc[VEOF]) should_echo = false;
if (should_echo)
{
char caret_notation[3] = { '^', '\0', '\0' };
if (key == 0x7f) caret_notation[1] = '?';
else
caret_notation[1] = key + 0x40;
for (int i = 0; i < 2; i++) { putchar(caret_notation[i]); }
}
else
putchar(key);
}
}
else
putchar(key);
write(m_pty, &request.letter, 1);
return ui::EventResult::DidHandle;
}
@ -223,8 +115,6 @@ Result<bool> TerminalWidget::process()
return err(errno);
}
query_termios();
bool should_update_cursor = tick_cursor();
ssize_t drawn = 0;
@ -263,16 +153,6 @@ bool TerminalWidget::tick_cursor()
return false;
}
void TerminalWidget::query_termios()
{
tcgetattr(m_pty, &m_settings);
}
bool TerminalWidget::is_canonical()
{
return m_settings.c_lflag & ICANON;
}
void TerminalWidget::draw_glyph(wchar_t c, int x, int y)
{
auto subcanvas = m_terminal_canvas.subcanvas({ x, y, m_font->width(), m_font->height() });

View File

@ -47,16 +47,11 @@ class TerminalWidget : public ui::Widget
ui::Color m_foreground_color { ui::WHITE };
ui::Color m_background_color { ui::BLACK };
void query_termios();
bool tick_cursor();
Utf8StateDecoder m_decoder;
Option<EscapeSequenceParser> m_escape_parser;
Result<ui::EventResult> handle_input(char key);
bool is_canonical();
void draw_glyph(wchar_t c, int x, int y);
void erase_current_line();
void scroll();