From 4c35c8d7fd4c593e8211d65be9a298ec27dcfa52 Mon Sep 17 00:00:00 2001 From: Drew Stratford Date: Sun, 20 Oct 2019 19:12:00 +1300 Subject: [PATCH] TTY: Implement Canonical mode and basic echoing. The TTY driver now respects the ICANON flag, enabling basic line editing like VKILL, VERASE, VEOF and VWERASE. Additionally, ICANON is now set by default. Basic echoing has can now be enabled via the ECHO flag, though more complicated echoing like ECHOCTL or ECHONL has not been implemented. --- Kernel/TTY/SlavePTY.cpp | 7 ++ Kernel/TTY/SlavePTY.h | 1 + Kernel/TTY/TTY.cpp | 125 +++++++++++++++++++++++++++++++++++- Kernel/TTY/TTY.h | 18 +++++- Kernel/TTY/VirtualConsole.h | 1 + 5 files changed, 148 insertions(+), 4 deletions(-) diff --git a/Kernel/TTY/SlavePTY.cpp b/Kernel/TTY/SlavePTY.cpp index 63a9932241..23a0378d1d 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 03bd892f19..0659079acf 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 1e71a7547e..0ace3b2454 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 6a9d47fa2f..f4bdad1567 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 b663d61b81..d62b98c76e 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"; }