kernel+init+preinit+wind: Remove the kernel TTY and support only userspace terminals
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
Still allow printing text to the console, but without text input or ANSI escape fancy stuff.
This commit is contained in:
parent
b52f96ab64
commit
36fad85396
@ -52,6 +52,16 @@ static void do_log(const char* format, ...)
|
|||||||
va_end(ap);
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void do_errlog(const char* format, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, format);
|
||||||
|
|
||||||
|
vfprintf(stderr, format, ap);
|
||||||
|
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
// Request a successful exit from the system (for tests)
|
// Request a successful exit from the system (for tests)
|
||||||
void sigterm_handler(int)
|
void sigterm_handler(int)
|
||||||
{
|
{
|
||||||
@ -121,7 +131,7 @@ static Result<void> try_start_service(Service& service)
|
|||||||
auto rc = service_child(service, new_stdout, new_stderr, new_stdin);
|
auto rc = service_child(service, new_stdout, new_stderr, new_stdin);
|
||||||
if (rc.has_error())
|
if (rc.has_error())
|
||||||
{
|
{
|
||||||
do_log("[child %d] failed to start service %s due to error: %s\n", getpid(), service.name.chars(),
|
do_errlog("[child %d] failed to start service %s due to error: %s\n", getpid(), service.name.chars(),
|
||||||
rc.error_string());
|
rc.error_string());
|
||||||
}
|
}
|
||||||
fclose(g_init_log);
|
fclose(g_init_log);
|
||||||
@ -150,7 +160,7 @@ static void start_service(Service& service)
|
|||||||
auto rc = try_start_service(service);
|
auto rc = try_start_service(service);
|
||||||
if (rc.has_error())
|
if (rc.has_error())
|
||||||
{
|
{
|
||||||
do_log("[init] failed to start service %s due to error: %s\n", service.name.chars(), rc.error_string());
|
do_errlog("[init] failed to start service %s due to error: %s\n", service.name.chars(), rc.error_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -358,9 +368,9 @@ Result<int> sysinit(StringView path)
|
|||||||
|
|
||||||
// Before this point, we don't even have an stdin, stdout and stderr. Set it up now so that child processes (and us)
|
// Before this point, we don't even have an stdin, stdout and stderr. Set it up now so that child processes (and us)
|
||||||
// can print stuff.
|
// can print stuff.
|
||||||
stdin = fopen("/dev/console", "r");
|
stdin = fopen("/dev/null", "r");
|
||||||
stdout = fopen("/dev/console", "w");
|
stdout = fopen("/dev/kmsg", "w");
|
||||||
stderr = fopen("/dev/console", "w");
|
stderr = fopen("/dev/kmsg", "w");
|
||||||
|
|
||||||
TRY(os::Security::pledge("stdio rpath wpath cpath fattr host mount proc exec id", nullptr));
|
TRY(os::Security::pledge("stdio rpath wpath cpath fattr host mount proc exec id", nullptr));
|
||||||
|
|
||||||
@ -377,8 +387,8 @@ Result<int> sysinit(StringView path)
|
|||||||
|
|
||||||
set_hostname();
|
set_hostname();
|
||||||
|
|
||||||
if (signal(SIGTERM, sigterm_handler) == SIG_ERR) do_log("[init] failed to register handler for SIGTERM\n");
|
if (signal(SIGTERM, sigterm_handler) == SIG_ERR) do_errlog("[init] failed to register handler for SIGTERM\n");
|
||||||
if (signal(SIGQUIT, sigquit_handler) == SIG_ERR) do_log("[init] failed to register handler for SIGQUIT\n");
|
if (signal(SIGQUIT, sigquit_handler) == SIG_ERR) do_errlog("[init] failed to register handler for SIGQUIT\n");
|
||||||
|
|
||||||
TRY(os::Security::pledge("stdio rpath wpath cpath proc exec id", nullptr));
|
TRY(os::Security::pledge("stdio rpath wpath cpath proc exec id", nullptr));
|
||||||
|
|
||||||
|
@ -19,9 +19,9 @@ static void mount_devfs()
|
|||||||
|
|
||||||
static void open_std_streams()
|
static void open_std_streams()
|
||||||
{
|
{
|
||||||
stdin = fopen("/dev/console", "r");
|
stdin = fopen("/dev/null", "r");
|
||||||
stdout = fopen("/dev/console", "w");
|
stdout = fopen("/dev/kmsg", "w");
|
||||||
stderr = fopen("/dev/console", "w");
|
stderr = fopen("/dev/kmsg", "w");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fail(const char* message)
|
static void fail(const char* message)
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
#include "arch/Timer.h"
|
#include "arch/Timer.h"
|
||||||
#include "arch/x86_64/CPU.h"
|
#include "arch/x86_64/CPU.h"
|
||||||
#include "arch/x86_64/IO.h"
|
#include "arch/x86_64/IO.h"
|
||||||
#include "fs/devices/ConsoleDevice.h"
|
#include "fs/devices/KeyboardDevice.h"
|
||||||
#include "fs/devices/MouseDevice.h"
|
#include "fs/devices/MouseDevice.h"
|
||||||
#include "memory/MemoryManager.h"
|
#include "memory/MemoryManager.h"
|
||||||
#include "sys/Syscall.h"
|
#include "sys/Syscall.h"
|
||||||
@ -151,7 +151,12 @@ void io_thread()
|
|||||||
u8 scancode;
|
u8 scancode;
|
||||||
moon::MousePacket packet;
|
moon::MousePacket packet;
|
||||||
|
|
||||||
if (scancode_queue.try_pop(scancode)) { ConsoleDevice::did_press_or_release_key(scancode); }
|
if (scancode_queue.try_pop(scancode))
|
||||||
|
{
|
||||||
|
static Keyboard::KeyboardState state = {};
|
||||||
|
auto code = Keyboard::decode_scancode(scancode, state);
|
||||||
|
if (code.has_value()) KeyboardDevice::add_keyboard_event(code.release_value());
|
||||||
|
}
|
||||||
else if (mouse_queue.try_pop(packet)) { MouseDevice::add_mouse_event(packet); }
|
else if (mouse_queue.try_pop(packet)) { MouseDevice::add_mouse_event(packet); }
|
||||||
else
|
else
|
||||||
kernel_wait_for_event();
|
kernel_wait_for_event();
|
||||||
@ -161,11 +166,7 @@ void io_thread()
|
|||||||
static void timer_interrupt(Registers* regs, void*)
|
static void timer_interrupt(Registers* regs, void*)
|
||||||
{
|
{
|
||||||
Timer::tick();
|
Timer::tick();
|
||||||
if (should_invoke_scheduler())
|
if (should_invoke_scheduler()) Scheduler::invoke(regs);
|
||||||
{
|
|
||||||
Scheduler::invoke(regs);
|
|
||||||
TextConsole::tick_cursor();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void keyboard_interrupt(Registers*, void*)
|
static void keyboard_interrupt(Registers*, void*)
|
||||||
|
@ -12,246 +12,19 @@
|
|||||||
#include <luna/Units.h>
|
#include <luna/Units.h>
|
||||||
#include <luna/Vector.h>
|
#include <luna/Vector.h>
|
||||||
|
|
||||||
Vector<SharedPtr<ConsoleDevice>> ConsoleDevice::m_console_devices;
|
|
||||||
bool ConsoleDevice::s_is_in_graphical_mode { false };
|
|
||||||
|
|
||||||
Result<void> ConsoleDevice::create()
|
Result<void> ConsoleDevice::create()
|
||||||
{
|
{
|
||||||
auto device = TRY(make_shared<ConsoleDevice>());
|
auto device = TRY(make_shared<ConsoleDevice>());
|
||||||
device->m_settings.c_lflag = ECHO | ECHOE | ECHOCTL | ISIG | ICANON;
|
return DeviceRegistry::register_special_device(DeviceRegistry::Console, 0, device, 0200);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<void> ConsoleDevice::handle_background_process_group(bool can_succeed, int signo) const
|
Result<usize> ConsoleDevice::read(u8*, usize, usize) const
|
||||||
{
|
{
|
||||||
if (!m_foreground_process_group.has_value()) return {};
|
return 0;
|
||||||
|
|
||||||
auto foreground_pgrp = m_foreground_process_group.value();
|
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
|
||||||
if (current->pgid == foreground_pgrp) return {};
|
|
||||||
|
|
||||||
if ((current->signal_mask.get(signo - 1)) || (current->signal_handlers[signo - 1].sa_handler == SIG_IGN))
|
|
||||||
{
|
|
||||||
if (can_succeed) return {};
|
|
||||||
return err(EIO);
|
|
||||||
}
|
|
||||||
|
|
||||||
current->send_signal(signo);
|
|
||||||
|
|
||||||
if (can_succeed) return err(EINTR);
|
|
||||||
return err(EIO);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<usize> ConsoleDevice::read(u8* buf, usize, usize length) const
|
|
||||||
{
|
|
||||||
TRY(handle_background_process_group(false, SIGTTIN));
|
|
||||||
|
|
||||||
length = m_input_buffer.dequeue_data(buf, length);
|
|
||||||
|
|
||||||
if (!length && m_may_read_without_blocking) m_may_read_without_blocking = false;
|
|
||||||
|
|
||||||
return length;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<usize> ConsoleDevice::write(const u8* buf, usize, usize length)
|
Result<usize> ConsoleDevice::write(const u8* buf, usize, usize length)
|
||||||
{
|
{
|
||||||
if (m_settings.c_lflag & TOSTOP) TRY(handle_background_process_group(true, SIGTTOU));
|
|
||||||
|
|
||||||
if (s_is_in_graphical_mode) return length;
|
|
||||||
|
|
||||||
TextConsole::write((const char*)buf, length);
|
TextConsole::write((const char*)buf, length);
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ConsoleDevice::will_block_if_read() const
|
|
||||||
{
|
|
||||||
return m_may_read_without_blocking ? false : m_input_buffer.size() == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConsoleDevice::did_press_or_release_key(u8 scancode)
|
|
||||||
{
|
|
||||||
if (!s_is_in_graphical_mode)
|
|
||||||
for (const auto& device : m_console_devices) { device->process_key_event(scancode); }
|
|
||||||
else
|
|
||||||
{
|
|
||||||
static Keyboard::KeyboardState state = {};
|
|
||||||
auto packet = Keyboard::decode_scancode(scancode, state);
|
|
||||||
if (packet.has_value()) KeyboardDevice::add_keyboard_event(packet.release_value());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (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))
|
|
||||||
{
|
|
||||||
TextConsole::putwchar(L'\b');
|
|
||||||
if (_iscntrl(maybe_char.value())) TextConsole::putwchar(L'\b');
|
|
||||||
if (maybe_char.value() == '\t') TextConsole::wprint(L"\b\b");
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((m_settings.c_lflag & ECHOE)) return;
|
|
||||||
else
|
|
||||||
is_special_character = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key == m_settings.c_cc[VEOF])
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (!is_special_character)
|
|
||||||
{
|
|
||||||
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 (!(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)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
TextConsole::putchar(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<u64> ConsoleDevice::ioctl(int request, void* arg)
|
|
||||||
{
|
|
||||||
auto* current = Scheduler::current();
|
|
||||||
TRY(check_pledge(current, Promise::p_tty));
|
|
||||||
|
|
||||||
switch (request)
|
|
||||||
{
|
|
||||||
case TCGETS: {
|
|
||||||
|
|
||||||
return MemoryManager::copy_to_user_typed((struct termios*)arg, &m_settings) ? 0 : err(EFAULT);
|
|
||||||
}
|
|
||||||
case TCSETS: {
|
|
||||||
TRY(handle_background_process_group(true, SIGTTOU));
|
|
||||||
|
|
||||||
if (!MemoryManager::copy_from_user_typed((const struct termios*)arg, &m_settings)) return err(EFAULT);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
case TIOCSPGRP: {
|
|
||||||
TRY(handle_background_process_group(true, SIGTTOU));
|
|
||||||
|
|
||||||
pid_t pgid;
|
|
||||||
if (!MemoryManager::copy_from_user_typed((const pid_t*)arg, &pgid)) return err(EFAULT);
|
|
||||||
|
|
||||||
bool pgid_exists = false;
|
|
||||||
Scheduler::for_each_in_process_group(pgid, [&pgid_exists](Thread*) {
|
|
||||||
pgid_exists = true;
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
if (!pgid_exists) return err(EPERM);
|
|
||||||
|
|
||||||
m_foreground_process_group = pgid;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
case TIOCGPGRP: {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
case TIOCGWINSZ: {
|
|
||||||
struct winsize window;
|
|
||||||
window.ws_col = TextConsole::cols();
|
|
||||||
window.ws_row = TextConsole::rows();
|
|
||||||
if (!MemoryManager::copy_to_user_typed((struct winsize*)arg, &window)) return err(EFAULT);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
case TTYSETGFX: {
|
|
||||||
s_is_in_graphical_mode = (bool)arg;
|
|
||||||
if (!s_is_in_graphical_mode) TextConsole::enable_cursor();
|
|
||||||
else
|
|
||||||
TextConsole::disable_cursor();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
default: return err(EINVAL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -14,42 +14,15 @@ class ConsoleDevice : public Device
|
|||||||
|
|
||||||
Result<usize> write(const u8*, usize, usize) override;
|
Result<usize> write(const u8*, usize, usize) override;
|
||||||
|
|
||||||
static void did_press_or_release_key(u8 scancode);
|
bool will_block_if_read() const override
|
||||||
|
{
|
||||||
bool will_block_if_read() const override;
|
return false;
|
||||||
|
}
|
||||||
Result<u64> ioctl(int request, void* arg) override;
|
|
||||||
|
|
||||||
StringView device_path() const override
|
StringView device_path() const override
|
||||||
{
|
{
|
||||||
return "console";
|
return "kmsg";
|
||||||
}
|
|
||||||
|
|
||||||
Result<u64> isatty() const override
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~ConsoleDevice() = default;
|
virtual ~ConsoleDevice() = default;
|
||||||
|
|
||||||
private:
|
|
||||||
struct termios m_settings;
|
|
||||||
mutable Buffer m_input_buffer;
|
|
||||||
Option<pid_t> m_foreground_process_group {};
|
|
||||||
Vector<u8> m_line_buffer;
|
|
||||||
mutable Keyboard::TTYKeyboardState m_kb_state;
|
|
||||||
|
|
||||||
static Vector<SharedPtr<ConsoleDevice>> m_console_devices;
|
|
||||||
static bool s_is_in_graphical_mode;
|
|
||||||
|
|
||||||
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<void> handle_background_process_group(bool can_succeed, int signo) const;
|
|
||||||
};
|
};
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
#include "video/Framebuffer.h"
|
#include "video/Framebuffer.h"
|
||||||
#include <luna/CString.h>
|
#include <luna/CString.h>
|
||||||
#include <luna/CType.h>
|
#include <luna/CType.h>
|
||||||
#include <luna/EscapeSequence.h>
|
|
||||||
#include <luna/Format.h>
|
#include <luna/Format.h>
|
||||||
#include <luna/Result.h>
|
#include <luna/Result.h>
|
||||||
#include <luna/ScopeGuard.h>
|
#include <luna/ScopeGuard.h>
|
||||||
@ -16,25 +15,7 @@ extern const BOOTBOOT bootboot;
|
|||||||
|
|
||||||
// Default text color.
|
// Default text color.
|
||||||
static constexpr u32 WHITE = 0xffffffff;
|
static constexpr u32 WHITE = 0xffffffff;
|
||||||
|
|
||||||
// xterm color palette.
|
|
||||||
static constexpr u32 BLACK = 0xff000000;
|
static constexpr u32 BLACK = 0xff000000;
|
||||||
static constexpr u32 RED = 0xffcd0000;
|
|
||||||
static constexpr u32 GREEN = 0xff00cd00;
|
|
||||||
static constexpr u32 YELLOW = 0xffcdcd00;
|
|
||||||
static constexpr u32 BLUE = 0xff0000ee;
|
|
||||||
static constexpr u32 MAGENTA = 0xffcd00cd;
|
|
||||||
static constexpr u32 CYAN = 0xff00cdcd;
|
|
||||||
static constexpr u32 GRAY = 0xffe5e5e5;
|
|
||||||
|
|
||||||
static constexpr u32 BRIGHT_BLACK = 0xff7f7f7f;
|
|
||||||
static constexpr u32 BRIGHT_RED = 0xffff0000;
|
|
||||||
static constexpr u32 BRIGHT_GREEN = 0xff00ff00;
|
|
||||||
static constexpr u32 BRIGHT_YELLOW = 0xffffff00;
|
|
||||||
static constexpr u32 BRIGHT_BLUE = 0xff5c5cff;
|
|
||||||
static constexpr u32 BRIGHT_MAGENTA = 0xffff00ff;
|
|
||||||
static constexpr u32 BRIGHT_CYAN = 0xff00ffff;
|
|
||||||
static constexpr u32 BRIGHT_GRAY = 0xffffffff;
|
|
||||||
|
|
||||||
static u32 g_background_color = BLACK;
|
static u32 g_background_color = BLACK;
|
||||||
static u32 g_foreground_color = WHITE;
|
static u32 g_foreground_color = WHITE;
|
||||||
@ -42,20 +23,11 @@ static u32 g_foreground_color = WHITE;
|
|||||||
static constexpr u32 FONT_HEIGHT = 16;
|
static constexpr u32 FONT_HEIGHT = 16;
|
||||||
static constexpr u32 FONT_WIDTH = 8;
|
static constexpr u32 FONT_WIDTH = 8;
|
||||||
|
|
||||||
static bool bold = false;
|
|
||||||
|
|
||||||
static u32 g_x_position = 0;
|
static u32 g_x_position = 0;
|
||||||
static u32 g_y_position = 0;
|
static u32 g_y_position = 0;
|
||||||
|
|
||||||
static constexpr int CURSOR_TIMEOUT = 500;
|
|
||||||
static int current_cursor_timeout = CURSOR_TIMEOUT;
|
|
||||||
static bool cursor_activated = true;
|
|
||||||
static bool cursor_enabled = true;
|
|
||||||
|
|
||||||
static Utf8StateDecoder utf8_decoder;
|
static Utf8StateDecoder utf8_decoder;
|
||||||
|
|
||||||
static Option<EscapeSequenceParser> escape_sequence_parser;
|
|
||||||
|
|
||||||
static void putwchar_at(wchar_t c, u32 x, u32 y)
|
static void putwchar_at(wchar_t c, u32 x, u32 y)
|
||||||
{
|
{
|
||||||
const u8* glyph = &font[c * 16];
|
const u8* glyph = &font[c * 16];
|
||||||
@ -111,216 +83,11 @@ static void erase_current_char()
|
|||||||
Framebuffer::rect(g_x_position, g_y_position, FONT_WIDTH, FONT_HEIGHT, BLACK);
|
Framebuffer::rect(g_x_position, g_y_position, FONT_WIDTH, FONT_HEIGHT, BLACK);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void draw_cursor()
|
|
||||||
{
|
|
||||||
Framebuffer::rect(g_x_position, g_y_position, FONT_WIDTH, FONT_HEIGHT, WHITE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool at_end_of_screen()
|
static bool at_end_of_screen()
|
||||||
{
|
{
|
||||||
return (g_x_position + FONT_WIDTH) > Framebuffer::width();
|
return (g_x_position + FONT_WIDTH) > Framebuffer::width();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool handle_escape_sequence(wchar_t c)
|
|
||||||
{
|
|
||||||
auto rc = escape_sequence_parser->advance(static_cast<u8>(c));
|
|
||||||
if (rc.has_error())
|
|
||||||
{
|
|
||||||
escape_sequence_parser = Option<EscapeSequenceParser> {};
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!rc.value()) return true;
|
|
||||||
if (!escape_sequence_parser->valid())
|
|
||||||
{
|
|
||||||
escape_sequence_parser = Option<EscapeSequenceParser> {};
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto& params = escape_sequence_parser->parameters();
|
|
||||||
switch (escape_sequence_parser->code())
|
|
||||||
{
|
|
||||||
case EscapeCode::CursorUp: {
|
|
||||||
int lines = params.size() ? params[0] : 1;
|
|
||||||
int pixels = lines * FONT_HEIGHT;
|
|
||||||
if ((u32)pixels > g_y_position) g_y_position = 0;
|
|
||||||
else
|
|
||||||
g_y_position -= pixels;
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case EscapeCode::CursorDown: {
|
|
||||||
int lines = params.size() ? params[0] : 1;
|
|
||||||
int pixels = lines * FONT_HEIGHT;
|
|
||||||
if (pixels + g_y_position >= Framebuffer::height()) g_y_position = Framebuffer::height() - FONT_HEIGHT;
|
|
||||||
else
|
|
||||||
g_y_position += pixels;
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case EscapeCode::CursorBack: {
|
|
||||||
int chars = params.size() ? params[0] : 1;
|
|
||||||
int pixels = chars * FONT_WIDTH;
|
|
||||||
if ((u32)pixels > g_x_position) g_x_position = 0;
|
|
||||||
else
|
|
||||||
g_x_position -= pixels;
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case EscapeCode::CursorForward: {
|
|
||||||
int chars = params.size() ? params[0] : 1;
|
|
||||||
int pixels = chars * FONT_WIDTH;
|
|
||||||
if (pixels + g_x_position >= Framebuffer::width()) g_x_position = Framebuffer::width() - FONT_WIDTH;
|
|
||||||
else
|
|
||||||
g_x_position += pixels;
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case EscapeCode::CursorNextLine: {
|
|
||||||
int lines = params.size() ? params[0] : 1;
|
|
||||||
int pixels = lines * FONT_HEIGHT;
|
|
||||||
if ((u32)pixels > g_y_position) g_y_position = 0;
|
|
||||||
else
|
|
||||||
g_y_position -= pixels;
|
|
||||||
g_x_position = 0;
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case EscapeCode::CursorPreviousLine: {
|
|
||||||
int lines = params.size() ? params[0] : 1;
|
|
||||||
int pixels = lines * FONT_HEIGHT;
|
|
||||||
if (pixels + g_y_position >= Framebuffer::height()) g_y_position = Framebuffer::height() - FONT_HEIGHT;
|
|
||||||
else
|
|
||||||
g_y_position += pixels;
|
|
||||||
g_x_position = 0;
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case EscapeCode::CursorHorizontalAbsolute: {
|
|
||||||
int line = (params.size() ? params[0] : 1) - 1;
|
|
||||||
if (line < 0) break;
|
|
||||||
u32 position = line * FONT_HEIGHT;
|
|
||||||
if (position >= Framebuffer::height()) position = Framebuffer::height() - FONT_HEIGHT;
|
|
||||||
g_y_position = position;
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case EscapeCode::SetCursorPosition: {
|
|
||||||
int x = (params.size() ? params[0] : 1) - 1;
|
|
||||||
int y = (params.size() > 1 ? params[1] : 1) - 1;
|
|
||||||
if (x < 0 || y < 0) break;
|
|
||||||
u32 x_position = x * FONT_WIDTH;
|
|
||||||
if (x_position >= Framebuffer::width()) x_position = Framebuffer::width() - FONT_HEIGHT;
|
|
||||||
g_x_position = x_position;
|
|
||||||
u32 y_position = y * FONT_HEIGHT;
|
|
||||||
if (y_position >= Framebuffer::height()) y_position = Framebuffer::height() - FONT_HEIGHT;
|
|
||||||
g_y_position = y_position;
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case EscapeCode::SelectGraphicRendition: {
|
|
||||||
if (!params.size())
|
|
||||||
{
|
|
||||||
g_foreground_color = WHITE;
|
|
||||||
g_background_color = BLACK;
|
|
||||||
bold = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (usize i = 0; i < params.size(); i++)
|
|
||||||
{
|
|
||||||
int arg = params[i];
|
|
||||||
switch (arg)
|
|
||||||
{
|
|
||||||
case 0: {
|
|
||||||
g_foreground_color = BLACK;
|
|
||||||
g_background_color = WHITE;
|
|
||||||
bold = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 1: {
|
|
||||||
bold = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 22: {
|
|
||||||
bold = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 30: {
|
|
||||||
g_foreground_color = bold ? BRIGHT_BLACK : BLACK;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 31: {
|
|
||||||
g_foreground_color = bold ? BRIGHT_RED : RED;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 32: {
|
|
||||||
g_foreground_color = bold ? BRIGHT_GREEN : GREEN;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 33: {
|
|
||||||
g_foreground_color = bold ? BRIGHT_YELLOW : YELLOW;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 34: {
|
|
||||||
g_foreground_color = bold ? BRIGHT_BLUE : BLUE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 35: {
|
|
||||||
g_foreground_color = bold ? BRIGHT_MAGENTA : MAGENTA;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 36: {
|
|
||||||
g_foreground_color = bold ? BRIGHT_CYAN : CYAN;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 37: {
|
|
||||||
g_foreground_color = bold ? BRIGHT_GRAY : GRAY;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 39: {
|
|
||||||
g_foreground_color = WHITE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 40: {
|
|
||||||
g_background_color = bold ? BRIGHT_BLACK : BLACK;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 41: {
|
|
||||||
g_background_color = bold ? BRIGHT_RED : RED;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 42: {
|
|
||||||
g_background_color = bold ? BRIGHT_GREEN : GREEN;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 43: {
|
|
||||||
g_background_color = bold ? BRIGHT_YELLOW : YELLOW;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 44: {
|
|
||||||
g_background_color = bold ? BRIGHT_BLUE : BLUE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 45: {
|
|
||||||
g_background_color = bold ? BRIGHT_MAGENTA : MAGENTA;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 46: {
|
|
||||||
g_background_color = bold ? BRIGHT_CYAN : CYAN;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 47: {
|
|
||||||
g_background_color = bold ? BRIGHT_GRAY : GRAY;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 49: {
|
|
||||||
g_background_color = BLACK;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
|
|
||||||
escape_sequence_parser = Option<EscapeSequenceParser> {};
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace TextConsole
|
namespace TextConsole
|
||||||
{
|
{
|
||||||
void putwchar(wchar_t c)
|
void putwchar(wchar_t c)
|
||||||
@ -328,16 +95,6 @@ namespace TextConsole
|
|||||||
// Unprintable (not in the built-in font) characters get represented as a box
|
// Unprintable (not in the built-in font) characters get represented as a box
|
||||||
if (c > (wchar_t)255) c = (wchar_t)256;
|
if (c > (wchar_t)255) c = (wchar_t)256;
|
||||||
|
|
||||||
if (escape_sequence_parser.has_value())
|
|
||||||
{
|
|
||||||
if (handle_escape_sequence(c)) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Erase the current cursor.
|
|
||||||
if (cursor_enabled) erase_current_char();
|
|
||||||
|
|
||||||
bool should_draw_cursor = cursor_enabled;
|
|
||||||
|
|
||||||
switch (c)
|
switch (c)
|
||||||
{
|
{
|
||||||
case L'\n': {
|
case L'\n': {
|
||||||
@ -357,13 +114,6 @@ namespace TextConsole
|
|||||||
erase_current_char();
|
erase_current_char();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case L'\x1b':
|
|
||||||
case L'\x9b':
|
|
||||||
case L'\x90':
|
|
||||||
case L'\x9d':
|
|
||||||
escape_sequence_parser = EscapeSequenceParser { (u8)c };
|
|
||||||
should_draw_cursor = false;
|
|
||||||
break;
|
|
||||||
default: {
|
default: {
|
||||||
if (_iscntrl(c)) return;
|
if (_iscntrl(c)) return;
|
||||||
putwchar_at(c, g_x_position, g_y_position);
|
putwchar_at(c, g_x_position, g_y_position);
|
||||||
@ -376,42 +126,6 @@ namespace TextConsole
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (should_draw_cursor)
|
|
||||||
{
|
|
||||||
current_cursor_timeout = CURSOR_TIMEOUT;
|
|
||||||
cursor_activated = true;
|
|
||||||
draw_cursor();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void tick_cursor()
|
|
||||||
{
|
|
||||||
if (!cursor_enabled) return;
|
|
||||||
|
|
||||||
current_cursor_timeout--;
|
|
||||||
if (current_cursor_timeout == 0)
|
|
||||||
{
|
|
||||||
current_cursor_timeout = CURSOR_TIMEOUT;
|
|
||||||
cursor_activated = !cursor_activated;
|
|
||||||
|
|
||||||
if (cursor_activated) draw_cursor();
|
|
||||||
else
|
|
||||||
erase_current_char();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void disable_cursor()
|
|
||||||
{
|
|
||||||
cursor_enabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void enable_cursor()
|
|
||||||
{
|
|
||||||
cursor_enabled = true;
|
|
||||||
cursor_activated = true;
|
|
||||||
current_cursor_timeout = CURSOR_TIMEOUT;
|
|
||||||
draw_cursor();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<void> putchar(char c)
|
Result<void> putchar(char c)
|
||||||
|
@ -19,9 +19,6 @@ namespace TextConsole
|
|||||||
Result<void> println(const char* str);
|
Result<void> println(const char* str);
|
||||||
void wprintln(const wchar_t* str);
|
void wprintln(const wchar_t* str);
|
||||||
Result<usize> printf(const char* format, ...) _format(1, 2);
|
Result<usize> printf(const char* format, ...) _format(1, 2);
|
||||||
void tick_cursor();
|
|
||||||
void disable_cursor();
|
|
||||||
void enable_cursor();
|
|
||||||
|
|
||||||
u16 rows();
|
u16 rows();
|
||||||
u16 cols();
|
u16 cols();
|
||||||
|
@ -8,7 +8,4 @@
|
|||||||
#define FB_GET_HEIGHT 1
|
#define FB_GET_HEIGHT 1
|
||||||
#define FB_GET_SCANLINE 2
|
#define FB_GET_SCANLINE 2
|
||||||
|
|
||||||
/* TTY requests. */
|
|
||||||
#define TTYSETGFX 162
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -75,12 +75,6 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isatty(STDIN_FILENO))
|
|
||||||
{
|
|
||||||
os::eprintln("error: wind must be run from a TTY!");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto mouse = TRY(os::File::open("/dev/mouse", os::File::ReadOnly));
|
auto mouse = TRY(os::File::open("/dev/mouse", os::File::ReadOnly));
|
||||||
mouse->set_buffer(os::File::NotBuffered);
|
mouse->set_buffer(os::File::NotBuffered);
|
||||||
mouse->set_close_on_exec();
|
mouse->set_close_on_exec();
|
||||||
@ -94,8 +88,6 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
|
|
||||||
Mouse mouse_pointer { screen.canvas() };
|
Mouse mouse_pointer { screen.canvas() };
|
||||||
|
|
||||||
ioctl(STDIN_FILENO, TTYSETGFX, 1);
|
|
||||||
|
|
||||||
setpgid(0, 0);
|
setpgid(0, 0);
|
||||||
|
|
||||||
int fd = open("/dev/null", O_RDONLY);
|
int fd = open("/dev/null", O_RDONLY);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user