From f244728d33f77e2db379d9b9067f0f80f22e5fc5 Mon Sep 17 00:00:00 2001 From: Bananymous Date: Fri, 16 Dec 2022 03:53:55 +0200 Subject: [PATCH] Kernel: Add unicode support --- kernel/arch/i386/VESA.cpp | 83 ++++++++++++++++++++++++-------- kernel/arch/i386/tty.cpp | 61 +++++++++++++++++++++-- kernel/include/kernel/Keyboard.h | 2 +- kernel/include/kernel/VESA.h | 2 +- kernel/kernel/Keyboard.cpp | 71 +++++++++++++++++---------- kernel/kernel/Shell.cpp | 59 ++++++++++++++++++++--- 6 files changed, 218 insertions(+), 60 deletions(-) diff --git a/kernel/arch/i386/VESA.cpp b/kernel/arch/i386/VESA.cpp index 21dc71ad4..24c346c40 100644 --- a/kernel/arch/i386/VESA.cpp +++ b/kernel/arch/i386/VESA.cpp @@ -21,15 +21,15 @@ namespace VESA static uint32_t s_height = 0; static uint8_t s_mode = 0; - static void GraphicsPutCharAt(uint8_t ch, uint32_t x, uint32_t y, Color fg, Color bg); + static void GraphicsPutCharAt(uint16_t ch, uint32_t x, uint32_t y, Color fg, Color bg); static void GraphicsClear(Color color); static void GraphicsScrollLine(uint32_t line); - static void TextPutCharAt(uint8_t c, uint32_t x, uint32_t y, Color fg, Color bg); + static void TextPutCharAt(uint16_t ch, uint32_t x, uint32_t y, Color fg, Color bg); static void TextClear(Color color); static void TextScrollLine(uint32_t line); - void PutEntryAt(uint8_t ch, uint32_t x, uint32_t y, Color fg, Color bg) + void PutEntryAt(uint16_t ch, uint32_t x, uint32_t y, Color fg, Color bg) { if (x >= s_width) return; @@ -138,27 +138,32 @@ namespace VESA 0x00'FF'FF'FF, }; - static void GraphicsPutPixelAt(uint32_t offset, uint32_t color) + static void GraphicsSetPixel(uint32_t* address, uint32_t color) { - uint32_t* pixel = (uint32_t*)((uint8_t*)s_addr + offset); switch (s_bpp) { case 24: - *pixel = (*pixel & 0xFF000000) | color; + *address = (*address & 0xFF000000) | (color & 0x00FFFFFF); break; case 32: - *pixel = color; + *address = color; break; } } - static void GraphicsPutPixelAt(uint32_t x, uint32_t y, uint32_t color) + static uint32_t GraphicsGetPixel(uint32_t* address) { - uint32_t offset = y * s_pitch + (x * (s_bpp / 8)); - GraphicsPutPixelAt(offset, color); - } + switch (s_bpp) + { + case 24: + return *address & 0x00FFFFFF; + case 32: + return *address; + } + return 0; + } - static void GraphicsPutCharAt(uint8_t ch, uint32_t x, uint32_t y, Color fg, Color bg) + static void GraphicsPutCharAt(uint16_t ch, uint32_t x, uint32_t y, Color fg, Color bg) { // find correct bitmap uint32_t index = 0; @@ -176,24 +181,62 @@ namespace VESA uint32_t u32_fg = s_graphics_colors[(uint8_t)fg]; uint32_t u32_bg = s_graphics_colors[(uint8_t)bg]; - for (uint32_t cy = 0; cy < font.Height; cy++) - if (y * font.Height + cy < s_height) - for (uint32_t cx = 0; cx < font.Width; cx++) - if (x * font.Width + cx < s_width) - GraphicsPutPixelAt(x * font.Width + cx, y * font.Height + cy, glyph[cy] & (1 << (font.Width - cx - 1)) ? u32_fg : u32_bg); + uint32_t fx = x * font.Width; + uint32_t fy = y * font.Height; + + uint32_t row_addr = (uint32_t)s_addr + (fy * s_pitch) + (fx * (s_bpp / 8)); + for (uint32_t gy = 0; gy < font.Height; gy++) + { + if (fy + gy >= s_height) break; + uint32_t pixel_addr = row_addr; + for (uint32_t gx = 0; gx < font.Width; gx++) + { + if (fx + gx >= s_width) break; + GraphicsSetPixel((uint32_t*)pixel_addr, (glyph[gy] & (1 << (font.Width - gx - 1))) ? u32_fg : u32_bg); + pixel_addr += s_bpp / 8; + } + row_addr += s_pitch; + } } static void GraphicsClear(Color color) { uint32_t u32_color = s_graphics_colors[(uint8_t)color]; + uint32_t row_addr = (uint32_t)s_addr; + for (uint32_t y = 0; y < s_height; y++) + { + uint32_t pixel_addr = row_addr; for (uint32_t x = 0; x < s_width; x++) - GraphicsPutPixelAt(x, y, u32_color); + { + GraphicsSetPixel((uint32_t*)pixel_addr, u32_color); + pixel_addr += s_bpp / 8; + } + row_addr += s_pitch; + } } static void GraphicsScrollLine(uint32_t line) { - (void)line; + if (line >= s_height / font.Height) + return; + + uint32_t row_out = (uint32_t)s_addr + (line - 1) * font.Height * s_pitch; + uint32_t row_in = (uint32_t)s_addr + (line - 0) * font.Height * s_pitch; + + for (uint32_t y = 0; y < font.Height; y++) + { + uint32_t pixel_out = row_out; + uint32_t pixel_in = row_in; + for (uint32_t x = 0; x < s_width; x++) + { + GraphicsSetPixel((uint32_t*)pixel_out, GraphicsGetPixel((uint32_t*)pixel_in)); + pixel_out += s_bpp / 8; + pixel_in += s_bpp / 8; + } + row_out += s_pitch; + row_in += s_pitch; + } } @@ -210,7 +253,7 @@ namespace VESA return ((uint16_t)color << 8) | ch; } - static void TextPutCharAt(uint8_t ch, uint32_t x, uint32_t y, Color fg, Color bg) + static void TextPutCharAt(uint16_t ch, uint32_t x, uint32_t y, Color fg, Color bg) { uint32_t index = y * s_width + x; ((uint16_t*)s_addr)[index] = TextEntry(ch, TextColor(fg, bg)); diff --git a/kernel/arch/i386/tty.cpp b/kernel/arch/i386/tty.cpp index e867be10b..bee35eb02 100644 --- a/kernel/arch/i386/tty.cpp +++ b/kernel/arch/i386/tty.cpp @@ -73,6 +73,53 @@ namespace TTY update_cursor(); } + static uint16_t handle_unicode(uint8_t ch) + { + static uint8_t unicode_left = 0; + static uint16_t codepoint = 0; + + if (unicode_left) + { + if ((ch >> 6) == 0b10) + { + codepoint = (codepoint << 6) | ch; + unicode_left--; + if (unicode_left > 0) + return 0xFFFF; + return codepoint; + } + else + { + // invalid utf-8 + unicode_left = 0; + return 0x00; + } + } + else + { + if ((ch >> 3) == 0b11110) + { + unicode_left = 3; + codepoint = ch & 0b00000111; + return 0xFFFF; + } + if ((ch >> 4) == 0b1110) + { + unicode_left = 2; + codepoint = ch & 0b00001111; + return 0xFFFF; + } + if ((ch >> 5) == 0b110) + { + unicode_left = 1; + codepoint = ch & 0b00011111; + return 0xFFFF; + } + } + + return ch & 0x7F; + } + static void reset_ansi_escape() { s_ansi_escape_mode = '\0'; @@ -111,7 +158,7 @@ namespace TTY } } - static void handle_ansi_escape(char c) + static void handle_ansi_escape(uint16_t c) { switch (s_ansi_escape_mode) { @@ -228,13 +275,17 @@ namespace TTY } } - void putchar(char c) + void putchar(char _c) { + uint16_t cp = handle_unicode(_c); + if (cp == 0xFFFF) + return; + if (s_ansi_escape_mode) - return handle_ansi_escape(c); + return handle_ansi_escape(cp); // https://en.wikipedia.org/wiki/ANSI_escape_code - switch (c) + switch (cp) { case BEL: // TODO break; @@ -261,7 +312,7 @@ namespace TTY s_ansi_escape_mode = '\1'; break; default: - VESA::PutEntryAt(c, terminal_col, terminal_row, terminal_fg, terminal_bg); + VESA::PutEntryAt(cp, terminal_col, terminal_row, terminal_fg, terminal_bg); terminal_col++; break; } diff --git a/kernel/include/kernel/Keyboard.h b/kernel/include/kernel/Keyboard.h index 9c5f6f53a..b42f0880b 100644 --- a/kernel/include/kernel/Keyboard.h +++ b/kernel/include/kernel/Keyboard.h @@ -42,7 +42,7 @@ namespace Keyboard void register_key_event_callback(void(*callback)(KeyEvent)); - char key_event_to_ascii(KeyEvent); + const char* key_event_to_utf8(KeyEvent); void led_disco(); diff --git a/kernel/include/kernel/VESA.h b/kernel/include/kernel/VESA.h index 8ccc11c21..0e22295cf 100644 --- a/kernel/include/kernel/VESA.h +++ b/kernel/include/kernel/VESA.h @@ -26,7 +26,7 @@ namespace VESA }; bool Initialize(); - void PutEntryAt(uint8_t, uint32_t, uint32_t, Color, Color); + void PutEntryAt(uint16_t, uint32_t, uint32_t, Color, Color); void Clear(Color); void ScrollLine(uint32_t line); diff --git a/kernel/kernel/Keyboard.cpp b/kernel/kernel/Keyboard.cpp index 189c3bbc0..3e2994b39 100644 --- a/kernel/kernel/Keyboard.cpp +++ b/kernel/kernel/Keyboard.cpp @@ -85,30 +85,53 @@ namespace Keyboard static void (*s_key_event_callback)(KeyEvent) = nullptr; - static char s_key_to_char[] + static const char* s_key_to_utf8_lower[] { - '\0', '\0', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'A', 'A', 'O', + nullptr, nullptr, + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", + "å", "ä", "ö", - ',', ':', '.', ';', '-', '_', '\'', '*', '^', '~', - '!', '?', '"', '#', '%', '&', '/', '\\', '+', '=', - '(', ')', '[', ']', '{', '}', - '$', '\0', '\0', '\0', '\n', ' ', '\t', '\b', '<', '>', '\0', '`', '\0', '\0', '@', '|', - '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', + ",", ":", ".", ";", "-", "_", "'", "*", "^", "~", + "!", "?", "\"", "#", "%", "&", "/", "\\", "+", "=", + "(", ")", "[", "]", "{", "}", + "$", "£", "€", "¤", "\n", " ", "\t", nullptr, "<", ">", "´", "`", "§", "½", "@", "|", + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - ',', '+', '*', '/', '-', '\n', + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", + ",", "+", "*", "/", "-", "\n", - '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - - '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, }; - static_assert(sizeof(s_key_to_char) == static_cast(Key::Count)); + static_assert(sizeof(s_key_to_utf8_lower) == static_cast(Key::Count) * sizeof(*s_key_to_utf8_lower)); + + static const char* s_key_to_utf8_upper[] + { + nullptr, nullptr, + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", + "Å", "Ä", "Ö", + + ",", ":", ".", ";", "-", "_", "'", "*", "^", "~", + "!", "?", "\"", "#", "%", "&", "/", "\\", "+", "=", + "(", ")", "[", "]", "{", "}", + "$", "£", "€", "¤", "\n", " ", "\t", nullptr, "<", ">", "´", "`", "§", "½", "@", "|", + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", + ",", "+", "*", "/", "-", "\n", + + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + }; + static_assert(sizeof(s_key_to_utf8_upper) == static_cast(Key::Count) * sizeof(*s_key_to_utf8_upper)); static uint8_t wait_and_read() { @@ -436,15 +459,13 @@ namespace Keyboard s_key_event_callback = callback; } - char key_event_to_ascii(KeyEvent event) + const char* key_event_to_utf8(KeyEvent event) { - char res = s_key_to_char[static_cast(event.key)]; - - if (!(event.modifiers & (MOD_SHIFT | MOD_CAPS))) - if (res >= 'A' && res <= 'Z') - res = res - 'A' + 'a'; - - return res; + bool shift = event.modifiers & MOD_SHIFT; + bool caps = event.modifiers & MOD_CAPS; + if (shift ^ caps) + return s_key_to_utf8_upper[static_cast(event.key)]; + return s_key_to_utf8_lower[static_cast(event.key)]; } void led_disco() diff --git a/kernel/kernel/Shell.cpp b/kernel/kernel/Shell.cpp index 7c7ab720d..3bc4e0ecc 100644 --- a/kernel/kernel/Shell.cpp +++ b/kernel/kernel/Shell.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include namespace Kernel @@ -172,6 +173,47 @@ namespace Kernel kprintln("unrecognized command '{}'", arguments.Front()); } + static uint8_t GetLastLength(const BAN::String& string) + { + if (string.Empty()) + return 0; + + if (!(string[string.Size() - 1] & 0x80)) + return 1; + + if (string.Size() < 2) + return 1; + + if (((uint8_t)string[string.Size() - 2] >> 5) == 0b110 && + ((uint8_t)string[string.Size() - 1] >> 6) == 0b10) + { + return 2; + } + + if (string.Size() < 3) + return 1; + + if (((uint8_t)string[string.Size() - 3] >> 4) == 0b1110 && + ((uint8_t)string[string.Size() - 2] >> 6) == 0b10 && + ((uint8_t)string[string.Size() - 1] >> 6) == 0b10) + { + return 3; + } + + if (string.Size() < 4) + return 1; + + if ((string[string.Size() - 4] >> 3) == 0b11110 && + (string[string.Size() - 3] >> 6) == 0b10 && + (string[string.Size() - 2] >> 6) == 0b10 && + (string[string.Size() - 1] >> 6) == 0b10) + { + return 3; + } + + return 1; + } + void Shell::KeyEventCallback(Keyboard::KeyEvent event) { if (!event.pressed) @@ -184,7 +226,10 @@ namespace Kernel if (!m_buffer.Empty()) { kprint("\b \b", 3); - m_buffer.PopBack(); + + uint8_t last_len = GetLastLength(m_buffer); + for (uint8_t i = 0; i < last_len; i++) + m_buffer.PopBack(); } break; } @@ -200,21 +245,19 @@ namespace Kernel } case Keyboard::Key::Tab: - event.key = Keyboard::Key::Space; - // fall through + break; default: { - char ascii = Keyboard::key_event_to_ascii(event); - if (ascii) + const char* utf8 = Keyboard::key_event_to_utf8(event); + if (utf8) { - kprint("{}", ascii); - m_buffer.PushBack(ascii); + kprint("{}", utf8); + m_buffer.Append(utf8); } break; } } } - } \ No newline at end of file