diff --git a/kernel/src/fs/devices/MasterPTY.cpp b/kernel/src/fs/devices/MasterPTY.cpp index 6cf7dc20..7128e6d6 100644 --- a/kernel/src/fs/devices/MasterPTY.cpp +++ b/kernel/src/fs/devices/MasterPTY.cpp @@ -5,6 +5,7 @@ #include "fs/devpts/FileSystem.h" #include "memory/MemoryManager.h" #include "thread/Scheduler.h" +#include Result> MasterPTY::create_pair(int index) { @@ -57,6 +58,130 @@ Result MasterPTY::handle_background_process_group(bool can_succeed, int si return err(EIO); } +bool MasterPTY::is_canonical() +{ + return m_settings.c_lflag & ICANON; +} + +Result 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 MasterPTY::read(u8* buf, usize, usize length) const { length = m_buffer.dequeue_data(buf, length); @@ -66,7 +191,7 @@ Result MasterPTY::read(u8* buf, usize, usize length) const Result 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; } diff --git a/kernel/src/fs/devices/MasterPTY.h b/kernel/src/fs/devices/MasterPTY.h index a4131a09..2c7c0098 100644 --- a/kernel/src/fs/devices/MasterPTY.h +++ b/kernel/src/fs/devices/MasterPTY.h @@ -67,7 +67,15 @@ class MasterPTY : public VFS::DeviceInode mutable Option m_foreground_process_group; struct winsize m_window; + typedef Vector Line; + + Vector m_lines; + Line m_current_line_buffer; + Result handle_background_process_group(bool can_succeed, int signo) const; + Result handle_input(u8 key); + + bool is_canonical(); int m_index; diff --git a/kernel/src/fs/devices/SlavePTY.cpp b/kernel/src/fs/devices/SlavePTY.cpp index c5f7d4a7..93d32d5e 100644 --- a/kernel/src/fs/devices/SlavePTY.cpp +++ b/kernel/src/fs/devices/SlavePTY.cpp @@ -10,7 +10,15 @@ Result 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 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 SlavePTY::ioctl(int request, void* arg) { auto* current = Scheduler::current(); diff --git a/kernel/src/fs/devices/SlavePTY.h b/kernel/src/fs/devices/SlavePTY.h index dafba4f9..39fa91b4 100644 --- a/kernel/src/fs/devices/SlavePTY.h +++ b/kernel/src/fs/devices/SlavePTY.h @@ -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 { diff --git a/libluna/include/luna/Buffer.h b/libluna/include/luna/Buffer.h index 3a561eaf..0713d54f 100644 --- a/libluna/include/luna/Buffer.h +++ b/libluna/include/luna/Buffer.h @@ -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 }; }; diff --git a/terminal/TerminalWidget.cpp b/terminal/TerminalWidget.cpp index 52bc152a..703ecbf2 100644 --- a/terminal/TerminalWidget.cpp +++ b/terminal/TerminalWidget.cpp @@ -95,115 +95,7 @@ Result 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 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 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() }); diff --git a/terminal/TerminalWidget.h b/terminal/TerminalWidget.h index dcf18dad..c10c0e3e 100644 --- a/terminal/TerminalWidget.h +++ b/terminal/TerminalWidget.h @@ -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 m_escape_parser; - Result handle_input(char key); - - bool is_canonical(); void draw_glyph(wchar_t c, int x, int y); void erase_current_line(); void scroll();