diff --git a/Kernel/TTY/SlavePTY.cpp b/Kernel/TTY/SlavePTY.cpp index 63a9932241f..23a0378d1df 100644 --- a/Kernel/TTY/SlavePTY.cpp +++ b/Kernel/TTY/SlavePTY.cpp @@ -30,6 +30,13 @@ StringView SlavePTY::tty_name() const return m_tty_name; } +void SlavePTY::echo(u8 ch) +{ + if (should_echo_input()) { + m_master->on_slave_write(&ch, 1); + } +} + void SlavePTY::on_master_write(const u8* buffer, ssize_t size) { for (ssize_t i = 0; i < size; ++i) diff --git a/Kernel/TTY/SlavePTY.h b/Kernel/TTY/SlavePTY.h index 03bd892f191..0659079acf2 100644 --- a/Kernel/TTY/SlavePTY.h +++ b/Kernel/TTY/SlavePTY.h @@ -16,6 +16,7 @@ private: // ^TTY virtual StringView tty_name() const override; virtual ssize_t on_tty_write(const u8*, ssize_t) override; + virtual void echo(u8) override; // ^CharacterDevice virtual bool can_read(FileDescription&) const override; diff --git a/Kernel/TTY/TTY.cpp b/Kernel/TTY/TTY.cpp index 1e71a7547e8..0ace3b24541 100644 --- a/Kernel/TTY/TTY.cpp +++ b/Kernel/TTY/TTY.cpp @@ -19,8 +19,8 @@ TTY::~TTY() void TTY::set_default_termios() { memset(&m_termios, 0, sizeof(m_termios)); - m_termios.c_lflag |= ISIG | ECHO; - static const char default_cc[32] = "\003\034\177\025\004\0\1\0\021\023\032\0\022\017\027\026\0"; + m_termios.c_lflag |= ISIG | ECHO | ICANON; + static const char default_cc[32] = "\003\034\010\025\004\0\1\0\021\023\032\0\022\017\027\026\0"; memcpy(m_termios.c_cc, default_cc, sizeof(default_cc)); } @@ -29,6 +29,26 @@ ssize_t TTY::read(FileDescription&, u8* buffer, ssize_t size) if (m_input_buffer.size() < size) size = m_input_buffer.size(); + if (in_canonical_mode()) { + int i = 0; + for (; i < size; i++) { + u8 ch = m_input_buffer.dequeue(); + if (ch == '\0') { + //Here we handle a ^D line, so we don't add the + //character to the output. + m_available_lines--; + break; + } else if (ch == '\n' || is_eol(ch)) { + buffer[i] = ch; + i++; + m_available_lines--; + break; + } + buffer[i] = ch; + } + return i; + } + for (int i = 0; i < size; i++) buffer[i] = m_input_buffer.dequeue(); @@ -50,6 +70,9 @@ ssize_t TTY::write(FileDescription&, const u8* buffer, ssize_t size) bool TTY::can_read(FileDescription&) const { + if (in_canonical_mode()) { + return m_available_lines > 0; + } return !m_input_buffer.is_empty(); } @@ -58,6 +81,31 @@ bool TTY::can_write(FileDescription&) const return true; } +bool TTY::is_eol(u8 ch) const +{ + return ch == m_termios.c_cc[VEOL]; +} + +bool TTY::is_eof(u8 ch) const +{ + return ch == m_termios.c_cc[VEOF]; +} + +bool TTY::is_kill(u8 ch) const +{ + return ch == m_termios.c_cc[VKILL]; +} + +bool TTY::is_erase(u8 ch) const +{ + return ch == m_termios.c_cc[VERASE]; +} + +bool TTY::is_werase(u8 ch) const +{ + return ch == m_termios.c_cc[VWERASE]; +} + void TTY::emit(u8 ch) { if (should_generate_signals()) { @@ -77,7 +125,80 @@ void TTY::emit(u8 ch) return; } } + + if (in_canonical_mode()) { + if (is_eof(ch)) { + m_available_lines++; + //We use '\0' to delimit the end + //of a line. + m_input_buffer.enqueue('\0'); + return; + } + if (is_kill(ch)) { + kill_line(); + return; + } + if (is_erase(ch)) { + do_backspace(); + return; + } + if (is_werase(ch)) { + erase_word(); + return; + } + if (ch == '\n' || is_eol(ch)) { + m_available_lines++; + } + } m_input_buffer.enqueue(ch); + echo(ch); +} + +bool TTY::can_do_backspace() const +{ + //can't do back space if we're empty. Plus, we don't want to + //removing any lines "commited" by newlines or ^D. + if (!m_input_buffer.is_empty() && !is_eol(m_input_buffer.last()) && m_input_buffer.last() != '\0') { + return true; + } + return false; +} + +void TTY::do_backspace() +{ + if (can_do_backspace()) { + m_input_buffer.dequeue_end(); + echo(m_termios.c_cc[VERASE]); + } +} + +// TODO: Currently, both erase_word() and kill_line work by sending +// a lot of VERASE characters; this is done because Terminal.cpp +// doesn't currently support VWERASE and VKILL. When these are +// implemented we could just send a VKILL or VWERASE. + +void TTY::erase_word() +{ + //Note: if we have leading whitespace before the word + //we want to delete we have to also delete that. + bool first_char = false; + while (can_do_backspace()) { + u8 ch = m_input_buffer.last(); + if (ch == ' ' && first_char) + break; + if (ch != ' ') + first_char = true; + m_input_buffer.dequeue_end(); + echo(m_termios.c_cc[VERASE]); + } +} + +void TTY::kill_line() +{ + while (can_do_backspace()) { + m_input_buffer.dequeue_end(); + echo(m_termios.c_cc[VERASE]); + } } void TTY::generate_signal(int signal) diff --git a/Kernel/TTY/TTY.h b/Kernel/TTY/TTY.h index 6a9d47fa2f4..f4bdad15671 100644 --- a/Kernel/TTY/TTY.h +++ b/Kernel/TTY/TTY.h @@ -1,7 +1,7 @@ #pragma once #include "DoubleBuffer.h" -#include +#include #include #include @@ -40,14 +40,28 @@ protected: TTY(unsigned major, unsigned minor); void emit(u8); + virtual void echo(u8) = 0; + + bool can_do_backspace() const; + void do_backspace(); + void erase_word(); + void kill_line(); + + bool is_eol(u8) const; + bool is_eof(u8) const; + bool is_kill(u8) const; + bool is_erase(u8) const; + bool is_werase(u8) const; void generate_signal(int signal); + int m_available_lines { 0 }; + private: // ^CharacterDevice virtual bool is_tty() const final override { return true; } - CircularQueue m_input_buffer; + CircularDeque m_input_buffer; pid_t m_pgid { 0 }; termios m_termios; unsigned short m_rows { 0 }; diff --git a/Kernel/TTY/VirtualConsole.h b/Kernel/TTY/VirtualConsole.h index b663d61b817..d62b98c76e0 100644 --- a/Kernel/TTY/VirtualConsole.h +++ b/Kernel/TTY/VirtualConsole.h @@ -33,6 +33,7 @@ private: // ^TTY virtual ssize_t on_tty_write(const u8*, ssize_t) override; virtual StringView tty_name() const override; + virtual void echo(u8) override { return; } // ^CharacterDevice virtual const char* class_name() const override { return "VirtualConsole"; }