diff --git a/apps/init.cpp b/apps/init.cpp index f7f4015b..f7e37013 100644 --- a/apps/init.cpp +++ b/apps/init.cpp @@ -52,6 +52,16 @@ static void do_log(const char* format, ...) 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) void sigterm_handler(int) { @@ -121,8 +131,8 @@ static Result try_start_service(Service& service) auto rc = service_child(service, new_stdout, new_stderr, new_stdin); if (rc.has_error()) { - do_log("[child %d] failed to start service %s due to error: %s\n", getpid(), service.name.chars(), - rc.error_string()); + do_errlog("[child %d] failed to start service %s due to error: %s\n", getpid(), service.name.chars(), + rc.error_string()); } fclose(g_init_log); exit(127); @@ -150,7 +160,7 @@ static void start_service(Service& service) auto rc = try_start_service(service); 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 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) // can print stuff. - stdin = fopen("/dev/console", "r"); - stdout = fopen("/dev/console", "w"); - stderr = fopen("/dev/console", "w"); + stdin = fopen("/dev/null", "r"); + stdout = fopen("/dev/kmsg", "w"); + stderr = fopen("/dev/kmsg", "w"); TRY(os::Security::pledge("stdio rpath wpath cpath fattr host mount proc exec id", nullptr)); @@ -377,8 +387,8 @@ Result sysinit(StringView path) set_hostname(); - if (signal(SIGTERM, sigterm_handler) == SIG_ERR) do_log("[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(SIGTERM, sigterm_handler) == SIG_ERR) do_errlog("[init] failed to register handler for SIGTERM\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)); diff --git a/apps/preinit.cpp b/apps/preinit.cpp index 95ef37ba..436d85d7 100644 --- a/apps/preinit.cpp +++ b/apps/preinit.cpp @@ -19,9 +19,9 @@ static void mount_devfs() static void open_std_streams() { - stdin = fopen("/dev/console", "r"); - stdout = fopen("/dev/console", "w"); - stderr = fopen("/dev/console", "w"); + stdin = fopen("/dev/null", "r"); + stdout = fopen("/dev/kmsg", "w"); + stderr = fopen("/dev/kmsg", "w"); } static void fail(const char* message) diff --git a/kernel/src/arch/x86_64/CPU.cpp b/kernel/src/arch/x86_64/CPU.cpp index f7bc7634..116c0038 100644 --- a/kernel/src/arch/x86_64/CPU.cpp +++ b/kernel/src/arch/x86_64/CPU.cpp @@ -5,7 +5,7 @@ #include "arch/Timer.h" #include "arch/x86_64/CPU.h" #include "arch/x86_64/IO.h" -#include "fs/devices/ConsoleDevice.h" +#include "fs/devices/KeyboardDevice.h" #include "fs/devices/MouseDevice.h" #include "memory/MemoryManager.h" #include "sys/Syscall.h" @@ -151,7 +151,12 @@ void io_thread() u8 scancode; 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 kernel_wait_for_event(); @@ -161,11 +166,7 @@ void io_thread() static void timer_interrupt(Registers* regs, void*) { Timer::tick(); - if (should_invoke_scheduler()) - { - Scheduler::invoke(regs); - TextConsole::tick_cursor(); - } + if (should_invoke_scheduler()) Scheduler::invoke(regs); } static void keyboard_interrupt(Registers*, void*) diff --git a/kernel/src/fs/devices/ConsoleDevice.cpp b/kernel/src/fs/devices/ConsoleDevice.cpp index 4037cf3c..cf1b4f7d 100644 --- a/kernel/src/fs/devices/ConsoleDevice.cpp +++ b/kernel/src/fs/devices/ConsoleDevice.cpp @@ -12,246 +12,19 @@ #include #include -Vector> ConsoleDevice::m_console_devices; -bool ConsoleDevice::s_is_in_graphical_mode { false }; - Result ConsoleDevice::create() { 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); + return DeviceRegistry::register_special_device(DeviceRegistry::Console, 0, device, 0200); } -Result ConsoleDevice::handle_background_process_group(bool can_succeed, int signo) const +Result ConsoleDevice::read(u8*, usize, usize) const { - if (!m_foreground_process_group.has_value()) return {}; - - 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 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; + return 0; } Result 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); 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 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); - } -} diff --git a/kernel/src/fs/devices/ConsoleDevice.h b/kernel/src/fs/devices/ConsoleDevice.h index a301cdd9..ca1aa1be 100644 --- a/kernel/src/fs/devices/ConsoleDevice.h +++ b/kernel/src/fs/devices/ConsoleDevice.h @@ -14,42 +14,15 @@ class ConsoleDevice : public Device Result write(const u8*, usize, usize) override; - static void did_press_or_release_key(u8 scancode); - - bool will_block_if_read() const override; - - Result ioctl(int request, void* arg) override; + bool will_block_if_read() const override + { + return false; + } StringView device_path() const override { - return "console"; - } - - Result isatty() const override - { - return 1; + return "kmsg"; } virtual ~ConsoleDevice() = default; - - private: - struct termios m_settings; - mutable Buffer m_input_buffer; - Option m_foreground_process_group {}; - Vector m_line_buffer; - mutable Keyboard::TTYKeyboardState m_kb_state; - - static Vector> 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 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 ea49ac90..cc034927 100644 --- a/kernel/src/video/TextConsole.cpp +++ b/kernel/src/video/TextConsole.cpp @@ -3,7 +3,6 @@ #include "video/Framebuffer.h" #include #include -#include #include #include #include @@ -16,25 +15,7 @@ extern const BOOTBOOT bootboot; // Default text color. static constexpr u32 WHITE = 0xffffffff; - -// xterm color palette. 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_foreground_color = WHITE; @@ -42,20 +23,11 @@ static u32 g_foreground_color = WHITE; static constexpr u32 FONT_HEIGHT = 16; static constexpr u32 FONT_WIDTH = 8; -static bool bold = false; - static u32 g_x_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 Option escape_sequence_parser; - static void putwchar_at(wchar_t c, u32 x, u32 y) { 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); } -static void draw_cursor() -{ - Framebuffer::rect(g_x_position, g_y_position, FONT_WIDTH, FONT_HEIGHT, WHITE); -} - static bool at_end_of_screen() { return (g_x_position + FONT_WIDTH) > Framebuffer::width(); } -static bool handle_escape_sequence(wchar_t c) -{ - auto rc = escape_sequence_parser->advance(static_cast(c)); - if (rc.has_error()) - { - escape_sequence_parser = Option {}; - return false; - } - if (!rc.value()) return true; - if (!escape_sequence_parser->valid()) - { - escape_sequence_parser = Option {}; - 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 {}; - return true; -} - namespace TextConsole { void putwchar(wchar_t c) @@ -328,16 +95,6 @@ namespace TextConsole // Unprintable (not in the built-in font) characters get represented as a box 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) { case L'\n': { @@ -357,13 +114,6 @@ namespace TextConsole erase_current_char(); } 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: { if (_iscntrl(c)) return; putwchar_at(c, g_x_position, g_y_position); @@ -376,42 +126,6 @@ namespace TextConsole 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 putchar(char c) diff --git a/kernel/src/video/TextConsole.h b/kernel/src/video/TextConsole.h index 4197dbb3..6e6f5287 100644 --- a/kernel/src/video/TextConsole.h +++ b/kernel/src/video/TextConsole.h @@ -19,9 +19,6 @@ namespace TextConsole Result println(const char* str); void wprintln(const wchar_t* str); Result printf(const char* format, ...) _format(1, 2); - void tick_cursor(); - void disable_cursor(); - void enable_cursor(); u16 rows(); u16 cols(); diff --git a/libc/include/bits/ioctl-defs.h b/libc/include/bits/ioctl-defs.h index 4d2e7298..29c20bde 100644 --- a/libc/include/bits/ioctl-defs.h +++ b/libc/include/bits/ioctl-defs.h @@ -8,7 +8,4 @@ #define FB_GET_HEIGHT 1 #define FB_GET_SCANLINE 2 -/* TTY requests. */ -#define TTYSETGFX 162 - #endif diff --git a/wind/main.cpp b/wind/main.cpp index b0b4930c..aa8d9cd7 100644 --- a/wind/main.cpp +++ b/wind/main.cpp @@ -75,12 +75,6 @@ Result luna_main(int argc, char** argv) 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)); mouse->set_buffer(os::File::NotBuffered); mouse->set_close_on_exec(); @@ -94,8 +88,6 @@ Result luna_main(int argc, char** argv) Mouse mouse_pointer { screen.canvas() }; - ioctl(STDIN_FILENO, TTYSETGFX, 1); - setpgid(0, 0); int fd = open("/dev/null", O_RDONLY);