diff --git a/apps/sh.cpp b/apps/sh.cpp index 80b66f1e..6da9da8e 100644 --- a/apps/sh.cpp +++ b/apps/sh.cpp @@ -98,7 +98,11 @@ Result luna_main(int argc, char** argv) auto maybe_cmd = input_file->read_line(); if (maybe_cmd.has_error()) { - if (maybe_cmd.error() == EINTR) continue; + if (maybe_cmd.error() == EINTR) + { + os::println(""); + continue; + } return maybe_cmd.release_error(); } @@ -150,6 +154,8 @@ Result luna_main(int argc, char** argv) { int sig = WTERMSIG(status); if (sig != SIGINT && sig != SIGQUIT) os::println("[sh] Process %d exited: %s", child, strsignal(sig)); + else + os::println(""); } } diff --git a/kernel/src/arch/Keyboard.h b/kernel/src/arch/Keyboard.h index 0fbdba67..f91c4ff6 100644 --- a/kernel/src/arch/Keyboard.h +++ b/kernel/src/arch/Keyboard.h @@ -3,13 +3,14 @@ namespace Keyboard { - enum Modifiers + struct KeyboardState { - LeftControl = 1, - LeftAlt = 2, + bool ignore_next { false }; + bool left_shift { false }; + bool right_shift { false }; + bool left_control { false }; + bool capslock { false }; }; - Option decode_scancode(u8 scancode); - - int modifiers(); + Option decode_scancode_tty(u8 scancode, KeyboardState& state); } diff --git a/kernel/src/arch/x86_64/CPU.cpp b/kernel/src/arch/x86_64/CPU.cpp index 4ef521ad..570f140f 100644 --- a/kernel/src/arch/x86_64/CPU.cpp +++ b/kernel/src/arch/x86_64/CPU.cpp @@ -147,8 +147,7 @@ void io_thread() u8 scancode; while (!scancode_queue.try_pop(scancode)) kernel_wait_for_event(); - char key; - if (Keyboard::decode_scancode(scancode).try_set_value(key)) ConsoleDevice::did_press_key(key); + ConsoleDevice::did_press_or_release_key(scancode); } } diff --git a/kernel/src/arch/x86_64/Keyboard.cpp b/kernel/src/arch/x86_64/Keyboard.cpp index 6488cd63..f8fccacf 100644 --- a/kernel/src/arch/x86_64/Keyboard.cpp +++ b/kernel/src/arch/x86_64/Keyboard.cpp @@ -1,4 +1,5 @@ #include "arch/Keyboard.h" +#include // PS/2 keyboard decoding routine. @@ -28,11 +29,6 @@ static bool should_ignore_key(u8 scancode) return (scancode > 0x3A && scancode < 0x47) || scancode == TAB || scancode == F11 || scancode == F12; } -static bool g_ignore_next { false }; -static bool g_left_shift { false }; -static bool g_right_shift { false }; -static bool g_capslock { false }; - constexpr char key_table[] = { '\0', '\1', // escape @@ -117,28 +113,26 @@ constexpr char shifted_key_table[] = { '.', // keypad . }; -static bool is_shifted() +static bool is_shifted(const Keyboard::KeyboardState& state) { - if (g_capslock) return !(g_left_shift || g_right_shift); - return g_left_shift || g_right_shift; + if (state.capslock) return !(state.left_shift || state.right_shift); + return state.left_shift || state.right_shift; } -int g_modifiers = 0; - namespace Keyboard { - Option decode_scancode(u8 scancode) + Option decode_scancode_tty(u8 scancode, KeyboardState& state) { - if (g_ignore_next) + if (state.ignore_next) { - g_ignore_next = false; + state.ignore_next = false; return {}; } // FIXME: Support extended scancodes. if (scancode == EXTENDED_KEY_CODE) { - g_ignore_next = true; + state.ignore_next = true; return {}; } @@ -146,35 +140,27 @@ namespace Keyboard if (scancode == LEFT_SHIFT) { - g_left_shift = !released; + state.left_shift = !released; return {}; } if (scancode == RIGHT_SHIFT) { - g_right_shift = !released; + state.right_shift = !released; return {}; } if (scancode == CAPS_LOCK) { - if (!released) g_capslock = !g_capslock; - return {}; - } - - if (scancode == LEFT_ALT) - { - if (released) g_modifiers &= ~LeftAlt; - else - g_modifiers |= LeftAlt; + if (!released) state.capslock = !state.capslock; return {}; } if (scancode == LEFT_CONTROL) { - if (released) g_modifiers &= ~LeftControl; + if (released) state.left_control = false; else - g_modifiers |= LeftControl; + state.left_control = true; return {}; } @@ -182,12 +168,20 @@ namespace Keyboard if (released) return {}; - if (is_shifted()) return shifted_key_table[scancode]; + if (state.left_control) + { + char key; + if (is_shifted(state)) key = shifted_key_table[scancode]; + else + key = key_table[scancode]; + if (_islower(key)) key = (char)_toupper(key); + if (_isupper(key)) return key - 0x40; + if (key == '@') return key - 0x40; + if (key > 'Z' && key < '`') return key - 0x40; + if (key == '?') return 0x7f; + } + + if (is_shifted(state)) return shifted_key_table[scancode]; return key_table[scancode]; } - - int modifiers() - { - return g_modifiers; - } } diff --git a/kernel/src/fs/devices/ConsoleDevice.cpp b/kernel/src/fs/devices/ConsoleDevice.cpp index 9f8affe5..997b06b1 100644 --- a/kernel/src/fs/devices/ConsoleDevice.cpp +++ b/kernel/src/fs/devices/ConsoleDevice.cpp @@ -1,36 +1,34 @@ #include "fs/devices/ConsoleDevice.h" #include "Log.h" -#include "arch/Keyboard.h" #include "fs/devices/DeviceRegistry.h" #include "memory/MemoryManager.h" #include "thread/Scheduler.h" #include "video/TextConsole.h" #include -#include -#include #include +#include #include #include -static Buffer g_console_input; -static bool g_eof { false }; -static bool g_echo { true }; -static bool g_stop_background_output { false }; - -static Option g_foreground_process_group; +Vector> ConsoleDevice::m_console_devices; Result ConsoleDevice::create() { - auto device = (SharedPtr)TRY(make_shared()); - g_foreground_process_group = {}; + auto device = TRY(make_shared()); + device->m_settings.c_lflag = ECHO | ECHOE | ECHOCTL | ISIG | ICANON; + device->m_settings.c_cc[VEOF] = '\4'; + device->m_settings.c_cc[VERASE] = '\b'; + device->m_settings.c_cc[VINTR] = '\3'; + device->m_settings.c_cc[VQUIT] = '\x1c'; + TRY(m_console_devices.try_append(device)); return DeviceRegistry::register_special_device(DeviceRegistry::Console, 0, device); } -static Result handle_background_process_group(bool can_succeed, int signo) +Result ConsoleDevice::handle_background_process_group(bool can_succeed, int signo) const { - if (!g_foreground_process_group.has_value()) return {}; + if (!m_foreground_process_group.has_value()) return {}; - auto foreground_pgrp = g_foreground_process_group.value(); + auto foreground_pgrp = m_foreground_process_group.value(); auto* current = Scheduler::current(); if ((pid_t)current->pgid == foreground_pgrp) return {}; @@ -51,22 +49,22 @@ Result ConsoleDevice::read(u8* buf, usize, usize length) const { TRY(handle_background_process_group(false, SIGTTIN)); - if (length > g_console_input.size()) length = g_console_input.size(); + if (length > m_input_buffer.size()) length = m_input_buffer.size(); - memcpy(buf, g_console_input.data(), length); + memcpy(buf, m_input_buffer.data(), length); - memmove(g_console_input.data(), g_console_input.data() + length, g_console_input.size() - length); + memmove(m_input_buffer.data(), m_input_buffer.data() + length, m_input_buffer.size() - length); - g_console_input.try_resize(g_console_input.size() - length).release_value(); + m_input_buffer.try_resize(m_input_buffer.size() - length).release_value(); - if (!length && g_eof) g_eof = false; + if (!length && m_may_read_without_blocking) m_may_read_without_blocking = false; return length; } Result ConsoleDevice::write(const u8* buf, usize, usize length) { - if (g_stop_background_output) TRY(handle_background_process_group(true, SIGTTOU)); + if (m_settings.c_lflag & TOSTOP) TRY(handle_background_process_group(true, SIGTTOU)); TextConsole::write((const char*)buf, length); return length; @@ -74,78 +72,124 @@ Result ConsoleDevice::write(const u8* buf, usize, usize length) bool ConsoleDevice::blocking() const { - return g_eof ? false : g_console_input.size() == 0; + return m_may_read_without_blocking ? false : m_input_buffer.size() == 0; } -static Vector g_temp_input; - -void ConsoleDevice::did_press_key(char key) +void ConsoleDevice::did_press_or_release_key(u8 scancode) { - if (key == '\b') + for (const auto& device : m_console_devices) { device->process_key_event(scancode); } +} + +void ConsoleDevice::process_key_event(u8 scancode) +{ + auto rc = Keyboard::decode_scancode_tty(scancode, m_kb_state); + if (!rc.has_value()) return; + char key = rc.value(); + check(key >= 0); + + bool is_special_character { false }; + + if (is_canonical()) { - if (g_temp_input.try_pop().has_value()) + if (key == m_settings.c_cc[VERASE]) { - if (g_echo) TextConsole::putwchar(L'\b'); + 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)) + { + TextConsole::putwchar(L'\b'); + if (_iscntrl(maybe_char.value())) TextConsole::putwchar(L'\b'); + } + return; + } + + if ((m_settings.c_lflag & ECHOE)) return; + else + is_special_character = true; } - return; - } - - // Ctrl+D - if (key == 'd' && (Keyboard::modifiers() == Keyboard::LeftControl)) - { - if (g_temp_input.size() == 0) g_eof = true; - return; - } - - if (key == 'c' && (Keyboard::modifiers() == Keyboard::LeftControl)) - { - g_temp_input.clear(); - - if (g_echo) TextConsole::wprintln(L"^C"); - - if (g_foreground_process_group.has_value()) + if (key == m_settings.c_cc[VEOF]) { - Scheduler::for_each_in_process_group(g_foreground_process_group.value(), [](Thread* thread) { - thread->send_signal(SIGINT); - return true; - }); + m_input_buffer.append_data(m_line_buffer.data(), m_line_buffer.size()); + m_line_buffer.clear(); + + m_may_read_without_blocking = true; + is_special_character = true; } - return; + if (m_settings.c_lflag & ISIG) + { + if (key == m_settings.c_cc[VINTR]) + { + if (!(m_settings.c_lflag & NOFLSH)) m_line_buffer.clear(); + + if (m_foreground_process_group.has_value()) + { + Scheduler::for_each_in_process_group(m_foreground_process_group.value(), [](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_line_buffer.clear(); + + if (m_foreground_process_group.has_value()) + { + Scheduler::for_each_in_process_group(m_foreground_process_group.value(), [](Thread* thread) { + thread->send_signal(SIGQUIT); + return true; + }); + } + is_special_character = true; + } + } } - if (key == 'e' && (Keyboard::modifiers() == (Keyboard::LeftAlt | Keyboard::LeftControl))) + if (!is_special_character) { - Scheduler::dump_state(); - return; + if (is_canonical()) + { + m_line_buffer.try_append((u8)key); + + if (key == '\n') + { + m_input_buffer.append_data(m_line_buffer.data(), m_line_buffer.size()); + m_line_buffer.clear(); + } + } + else + m_input_buffer.append_data((u8*)&key, 1); } - if (key == 'm' && (Keyboard::modifiers() == (Keyboard::LeftAlt | Keyboard::LeftControl))) + if (!(m_settings.c_lflag & ECHO)) return; + + if (_iscntrl(key)) { - kinfoln("Total memory: %s", to_dynamic_unit(MemoryManager::total()).release_value().chars()); - kinfoln("Free memory: %s", to_dynamic_unit(MemoryManager::free()).release_value().chars()); - kinfoln("Used memory: %s", to_dynamic_unit(MemoryManager::used()).release_value().chars()); - kinfoln("Reserved memory: %s", to_dynamic_unit(MemoryManager::reserved()).release_value().chars()); - return; + if (m_settings.c_lflag & ECHOCTL) + { + bool should_echo = true; + if (key == '\n' || 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; + + TextConsole::print(caret_notation); + } + else + TextConsole::putchar(key); + } } - - if (key == 'h' && (Keyboard::modifiers() == (Keyboard::LeftAlt | Keyboard::LeftControl))) - { - dump_heap_usage(); - return; - } - - g_temp_input.try_append((u8)key); - - if (key == '\n') - { - g_console_input.append_data(g_temp_input.data(), g_temp_input.size()); - g_temp_input.clear(); - } - - if (!g_echo) return; - TextConsole::putchar(key); + else + TextConsole::putchar(key); } Result ConsoleDevice::ioctl(int request, void* arg) @@ -153,27 +197,13 @@ Result ConsoleDevice::ioctl(int request, void* arg) switch (request) { case TCGETS: { - struct termios tc - { - .c_lflag = 0 - }; - if (g_echo) tc.c_lflag |= ECHO; - if (g_stop_background_output) tc.c_lflag |= TOSTOP; - return MemoryManager::copy_to_user_typed((struct termios*)arg, &tc) ? 0 : err(EFAULT); + return MemoryManager::copy_to_user_typed((struct termios*)arg, &m_settings) ? 0 : err(EFAULT); } case TCSETS: { TRY(handle_background_process_group(true, SIGTTOU)); - struct termios tc; - if (!MemoryManager::copy_from_user_typed((const struct termios*)arg, &tc)) return err(EFAULT); - if (tc.c_lflag & ECHO) g_echo = true; - else - g_echo = false; - - if (tc.c_lflag & TOSTOP) g_stop_background_output = true; - else - g_stop_background_output = false; + if (!MemoryManager::copy_from_user_typed((const struct termios*)arg, &m_settings)) return err(EFAULT); return 0; } @@ -190,11 +220,11 @@ Result ConsoleDevice::ioctl(int request, void* arg) }); if (!pgid_exists) return err(EPERM); - g_foreground_process_group = pgid; + m_foreground_process_group = pgid; return 0; } case TIOCGPGRP: { - pid_t pgid = g_foreground_process_group.value_or((pid_t)next_thread_id()); + pid_t pgid = m_foreground_process_group.value_or((pid_t)next_thread_id()); if (!MemoryManager::copy_to_user_typed((pid_t*)arg, &pgid)) return err(EFAULT); return 0; } diff --git a/kernel/src/fs/devices/ConsoleDevice.h b/kernel/src/fs/devices/ConsoleDevice.h index d9dddd62..0408a9e8 100644 --- a/kernel/src/fs/devices/ConsoleDevice.h +++ b/kernel/src/fs/devices/ConsoleDevice.h @@ -1,5 +1,8 @@ #pragma once +#include "arch/Keyboard.h" #include "fs/devices/DeviceRegistry.h" +#include +#include class ConsoleDevice : public Device { @@ -11,7 +14,7 @@ class ConsoleDevice : public Device Result write(const u8*, usize, usize) override; - static void did_press_key(char key); + static void did_press_or_release_key(u8 scancode); bool blocking() const override; @@ -28,4 +31,24 @@ class ConsoleDevice : public Device } virtual ~ConsoleDevice() = default; + + private: + struct termios m_settings; + mutable Buffer m_input_buffer; + Option m_foreground_process_group {}; + Vector m_line_buffer; + mutable Keyboard::KeyboardState m_kb_state; + + static Vector> m_console_devices; + + void process_key_event(u8 scancode); + + mutable bool m_may_read_without_blocking { false }; + + inline bool is_canonical() const + { + return m_settings.c_lflag & ICANON; + } + + Result handle_background_process_group(bool can_succeed, int signo) const; }; diff --git a/kernel/src/video/TextConsole.cpp b/kernel/src/video/TextConsole.cpp index 033941fd..d55f433c 100644 --- a/kernel/src/video/TextConsole.cpp +++ b/kernel/src/video/TextConsole.cpp @@ -101,6 +101,10 @@ namespace TextConsole if (should_scroll()) scroll(); break; } + case L'\t': { + wprint(L" "); + break; + } case L'\r': g_x_position = 0; break; case L'\b': if (g_x_position != 0) @@ -210,11 +214,11 @@ namespace TextConsole u16 rows() { - return Framebuffer::height() / FONT_HEIGHT; + return (u16)Framebuffer::height() / FONT_HEIGHT; } u16 cols() { - return Framebuffer::width() / FONT_WIDTH; + return (u16)Framebuffer::width() / FONT_WIDTH; } } diff --git a/libc/include/bits/fixed-size-types.h b/libc/include/bits/fixed-size-types.h index dde7116f..d38de035 100644 --- a/libc/include/bits/fixed-size-types.h +++ b/libc/include/bits/fixed-size-types.h @@ -3,7 +3,7 @@ #ifndef _BITS_FIXED_SIZE_TYPES_H #define _BITS_FIXED_SIZE_TYPES_H -#if !defined(_SYS_TYPES_H) && !defined(_BITS_SETJMP_TYPES_H) && !defined(_BITS_MAKEDEV_H) +#if !defined(_SYS_TYPES_H) && !defined(_BITS_SETJMP_TYPES_H) && !defined(_BITS_MAKEDEV_H) && !defined(_BITS_TERMIOS_H) #error "Never include bits/fixed-size-types.h, use the standard header instead." #endif diff --git a/libc/include/bits/termios.h b/libc/include/bits/termios.h index fd022569..d1506915 100644 --- a/libc/include/bits/termios.h +++ b/libc/include/bits/termios.h @@ -3,25 +3,41 @@ #ifndef _BITS_TERMIOS_H #define _BITS_TERMIOS_H +#include + typedef long tcflag_t; +typedef char cc_t; + +#define NCCS 4 + +#define VEOF 0 +#define VERASE 1 +#define VINTR 2 +#define VQUIT 3 // VERY incomplete termios structure. struct termios { tcflag_t c_lflag; + cc_t c_cc[NCCS]; }; struct winsize { - u16 ws_row; - u16 ws_col; - u16 ws_xpixel; - u16 ws_ypixel; + __u16_t ws_row; + __u16_t ws_col; + __u16_t ws_xpixel; + __u16_t ws_ypixel; }; // Values for c_lflag. #define ECHO 1 #define TOSTOP 2 +#define ECHOCTL 4 +#define ISIG 8 +#define ICANON 16 +#define ECHOE 32 +#define NOFLSH 64 // termios ioctl() requests. #define TCGETS 0 diff --git a/libc/include/unistd.h b/libc/include/unistd.h index a9548ef9..af3b60da 100644 --- a/libc/include/unistd.h +++ b/libc/include/unistd.h @@ -18,6 +18,7 @@ #define _POSIX_CHOWN_RESTRICTED 200112L #define _POSIX_SHELL 200112L +#define _POSIX_VDISABLE (-2) #ifdef __cplusplus extern "C"