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 "fs/devpts/FileSystem.h"
#include "memory/MemoryManager.h" #include "memory/MemoryManager.h"
#include "thread/Scheduler.h" #include "thread/Scheduler.h"
#include <luna/CType.h>
Result<SharedPtr<VFS::Inode>> MasterPTY::create_pair(int index) 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); 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 Result<usize> MasterPTY::read(u8* buf, usize, usize length) const
{ {
length = m_buffer.dequeue_data(buf, length); 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) 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; return length;
} }

View File

@ -67,7 +67,15 @@ class MasterPTY : public VFS::DeviceInode
mutable Option<pid_t> m_foreground_process_group; mutable Option<pid_t> m_foreground_process_group;
struct winsize m_window; 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_background_process_group(bool can_succeed, int signo) const;
Result<void> handle_input(u8 key);
bool is_canonical();
int m_index; 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)); 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; return length;
} }
@ -26,6 +34,13 @@ Result<usize> SlavePTY::write(const u8* buf, usize, usize length)
return 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) Result<u64> SlavePTY::ioctl(int request, void* arg)
{ {
auto* current = Scheduler::current(); auto* current = Scheduler::current();

View File

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

View File

@ -20,6 +20,16 @@ class Buffer
Buffer(); 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(Buffer&& other);
Buffer(const Buffer& other) = delete; // For now. Buffer(const Buffer& other) = delete; // For now.
@ -161,8 +171,6 @@ class Buffer
} }
private: private:
Buffer(u8* data, usize size);
u8* m_data { nullptr }; u8* m_data { nullptr };
usize m_size { 0 }; 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). // invalid zero byte into the terminal input (this would also happen on Shift or Ctrl keypresses).
if (request.letter == '\0') return ui::EventResult::DidNotHandle; if (request.letter == '\0') return ui::EventResult::DidNotHandle;
return handle_input(request.letter); write(m_pty, &request.letter, 1);
}
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);
return ui::EventResult::DidHandle; return ui::EventResult::DidHandle;
} }
@ -223,8 +115,6 @@ Result<bool> TerminalWidget::process()
return err(errno); return err(errno);
} }
query_termios();
bool should_update_cursor = tick_cursor(); bool should_update_cursor = tick_cursor();
ssize_t drawn = 0; ssize_t drawn = 0;
@ -263,16 +153,6 @@ bool TerminalWidget::tick_cursor()
return false; 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) 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() }); 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_foreground_color { ui::WHITE };
ui::Color m_background_color { ui::BLACK }; ui::Color m_background_color { ui::BLACK };
void query_termios();
bool tick_cursor(); bool tick_cursor();
Utf8StateDecoder m_decoder; Utf8StateDecoder m_decoder;
Option<EscapeSequenceParser> m_escape_parser; 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 draw_glyph(wchar_t c, int x, int y);
void erase_current_line(); void erase_current_line();
void scroll(); void scroll();