#include "video/TextConsole.h" #include "boot/bootboot.h" #include "video/Framebuffer.h" #include #include #include #include #include #include #include extern const BOOTBOOT bootboot; #include "video/BuiltinFont.h" static constexpr u32 BLACK = 0xff000000; static constexpr u32 WHITE = 0xffffffff; static u32 g_background_color = BLACK; static u32 g_foreground_color = WHITE; static constexpr u32 FONT_HEIGHT = 16; static constexpr u32 FONT_WIDTH = 8; static u32 g_x_position = 0; static u32 g_y_position = 0; static Utf8StateDecoder utf8_decoder; static void putwchar_at(wchar_t c, u32 x, u32 y) { const u8* glyph = &font[c * 16]; for (u32 i = 0; i < FONT_HEIGHT; i++) { for (u32 j = 0; j < FONT_WIDTH; j++) { volatile u8 mask = *glyph; if (mask & (0b10000000 >> j)) Framebuffer::pixel(x + j, y + i, g_foreground_color); else Framebuffer::pixel(x + j, y + i, g_background_color); } glyph++; } } static void erase_current_line() { Framebuffer::rect(0, g_y_position, Framebuffer::width(), FONT_HEIGHT, BLACK); } static void scroll() { memcpy(Framebuffer::ptr(), Framebuffer::ptr() + (Framebuffer::scanline() * FONT_HEIGHT), Framebuffer::size() - (Framebuffer::scanline() * FONT_HEIGHT)); g_y_position -= FONT_HEIGHT; erase_current_line(); } static bool should_scroll() { return (g_y_position + FONT_HEIGHT) >= Framebuffer::height(); } static void next_line() { g_x_position = 0; g_y_position += FONT_HEIGHT; } static void next_char() { g_x_position += FONT_WIDTH; } static void prev_char() { g_x_position -= FONT_WIDTH; } static void erase_current_char() { Framebuffer::rect(g_x_position, g_y_position, FONT_WIDTH, FONT_HEIGHT, BLACK); } static bool at_end_of_screen() { return (g_x_position + FONT_WIDTH) > Framebuffer::width(); } namespace TextConsole { void putwchar(wchar_t c) { // Unprintable (not in the built-in font) characters get represented as a box if (c > (wchar_t)255) c = (wchar_t)256; switch (c) { case L'\n': { next_line(); 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) { prev_char(); erase_current_char(); } break; default: { if (_iscntrl(c)) return; putwchar_at(c, g_x_position, g_y_position); next_char(); if (at_end_of_screen()) { next_line(); if (should_scroll()) scroll(); } break; } } } Result putchar(char c) { auto guard = make_scope_guard([] { utf8_decoder.reset(); }); bool is_ready = TRY(utf8_decoder.feed(c)); if (is_ready) putwchar(TRY(utf8_decoder.extract())); guard.deactivate(); return {}; } void set_foreground(u32 color) { g_foreground_color = color; } void set_background(u32 color) { g_background_color = color; } u32 foreground() { return g_foreground_color; } u32 background() { return g_background_color; } void move_to(u32 x, u32 y) { g_x_position = x; g_y_position = y; } void clear() { move_to(0, 0); Framebuffer::rect(0, 0, Framebuffer::width(), Framebuffer::height(), BLACK); } Result print(const char* str) { while (*str) TRY(putchar(*str++)); return {}; } Result write(const char* str, usize len) { while (len--) TRY(putchar(*str++)); return {}; } void wprint(const wchar_t* str) { while (*str) putwchar(*str++); } Result println(const char* str) { TRY(print(str)); putwchar(L'\n'); return {}; } void wprintln(const wchar_t* str) { wprint(str); putwchar(L'\n'); } Result printf(const char* format, ...) { va_list ap; va_start(ap, format); const usize rc = TRY(cstyle_format( format, [](char c, void*) -> Result { return putchar(c); }, nullptr, ap)); va_end(ap); return rc; } u16 rows() { return (u16)Framebuffer::height() / (u16)FONT_HEIGHT; } u16 cols() { return (u16)Framebuffer::width() / (u16)FONT_WIDTH; } }