Kernel: Add unicode support

This commit is contained in:
Bananymous 2022-12-16 03:53:55 +02:00
parent 6fe0d04f07
commit f244728d33
6 changed files with 218 additions and 60 deletions

View File

@ -21,15 +21,15 @@ namespace VESA
static uint32_t s_height = 0; static uint32_t s_height = 0;
static uint8_t s_mode = 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 GraphicsClear(Color color);
static void GraphicsScrollLine(uint32_t line); 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 TextClear(Color color);
static void TextScrollLine(uint32_t line); 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) if (x >= s_width)
return; return;
@ -138,27 +138,32 @@ namespace VESA
0x00'FF'FF'FF, 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) switch (s_bpp)
{ {
case 24: case 24:
*pixel = (*pixel & 0xFF000000) | color; *address = (*address & 0xFF000000) | (color & 0x00FFFFFF);
break; break;
case 32: case 32:
*pixel = color; *address = color;
break; 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)); switch (s_bpp)
GraphicsPutPixelAt(offset, color); {
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 // find correct bitmap
uint32_t index = 0; uint32_t index = 0;
@ -176,24 +181,62 @@ namespace VESA
uint32_t u32_fg = s_graphics_colors[(uint8_t)fg]; uint32_t u32_fg = s_graphics_colors[(uint8_t)fg];
uint32_t u32_bg = s_graphics_colors[(uint8_t)bg]; uint32_t u32_bg = s_graphics_colors[(uint8_t)bg];
for (uint32_t cy = 0; cy < font.Height; cy++) uint32_t fx = x * font.Width;
if (y * font.Height + cy < s_height) uint32_t fy = y * font.Height;
for (uint32_t cx = 0; cx < font.Width; cx++)
if (x * font.Width + cx < s_width) uint32_t row_addr = (uint32_t)s_addr + (fy * s_pitch) + (fx * (s_bpp / 8));
GraphicsPutPixelAt(x * font.Width + cx, y * font.Height + cy, glyph[cy] & (1 << (font.Width - cx - 1)) ? u32_fg : u32_bg); 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) static void GraphicsClear(Color color)
{ {
uint32_t u32_color = s_graphics_colors[(uint8_t)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++) for (uint32_t y = 0; y < s_height; y++)
{
uint32_t pixel_addr = row_addr;
for (uint32_t x = 0; x < s_width; x++) 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) 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; 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; uint32_t index = y * s_width + x;
((uint16_t*)s_addr)[index] = TextEntry(ch, TextColor(fg, bg)); ((uint16_t*)s_addr)[index] = TextEntry(ch, TextColor(fg, bg));

View File

@ -73,6 +73,53 @@ namespace TTY
update_cursor(); 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() static void reset_ansi_escape()
{ {
s_ansi_escape_mode = '\0'; 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) 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) if (s_ansi_escape_mode)
return handle_ansi_escape(c); return handle_ansi_escape(cp);
// https://en.wikipedia.org/wiki/ANSI_escape_code // https://en.wikipedia.org/wiki/ANSI_escape_code
switch (c) switch (cp)
{ {
case BEL: // TODO case BEL: // TODO
break; break;
@ -261,7 +312,7 @@ namespace TTY
s_ansi_escape_mode = '\1'; s_ansi_escape_mode = '\1';
break; break;
default: 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++; terminal_col++;
break; break;
} }

View File

@ -42,7 +42,7 @@ namespace Keyboard
void register_key_event_callback(void(*callback)(KeyEvent)); void register_key_event_callback(void(*callback)(KeyEvent));
char key_event_to_ascii(KeyEvent); const char* key_event_to_utf8(KeyEvent);
void led_disco(); void led_disco();

View File

@ -26,7 +26,7 @@ namespace VESA
}; };
bool Initialize(); 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 Clear(Color);
void ScrollLine(uint32_t line); void ScrollLine(uint32_t line);

View File

@ -85,30 +85,53 @@ namespace Keyboard
static void (*s_key_event_callback)(KeyEvent) = nullptr; static void (*s_key_event_callback)(KeyEvent) = nullptr;
static char s_key_to_char[] static const char* s_key_to_utf8_lower[]
{ {
'\0', '\0', nullptr, nullptr,
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', "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", "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', "å", "ä", "ö",
',', ':', '.', ';', '-', '_', '\'', '*', '^', '~', ",", ":", ".", ";", "-", "_", "'", "*", "^", "~",
'!', '?', '"', '#', '%', '&', '/', '\\', '+', '=', "!", "?", "\"", "#", "%", "&", "/", "\\", "+", "=",
'(', ')', '[', ']', '{', '}', "(", ")", "[", "]", "{", "}",
'$', '\0', '\0', '\0', '\n', ' ', '\t', '\b', '<', '>', '\0', '`', '\0', '\0', '@', '|', "$", "£", "", "¤", "\n", " ", "\t", 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,
'\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', "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
',', '+', '*', '/', '-', '\n', ",", "+", "*", "/", "-", "\n",
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
}; };
static_assert(sizeof(s_key_to_char) == static_cast<int>(Key::Count)); static_assert(sizeof(s_key_to_utf8_lower) == static_cast<int>(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<int>(Key::Count) * sizeof(*s_key_to_utf8_upper));
static uint8_t wait_and_read() static uint8_t wait_and_read()
{ {
@ -436,15 +459,13 @@ namespace Keyboard
s_key_event_callback = callback; 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<uint8_t>(event.key)]; bool shift = event.modifiers & MOD_SHIFT;
bool caps = event.modifiers & MOD_CAPS;
if (!(event.modifiers & (MOD_SHIFT | MOD_CAPS))) if (shift ^ caps)
if (res >= 'A' && res <= 'Z') return s_key_to_utf8_upper[static_cast<uint8_t>(event.key)];
res = res - 'A' + 'a'; return s_key_to_utf8_lower[static_cast<uint8_t>(event.key)];
return res;
} }
void led_disco() void led_disco()

View File

@ -6,6 +6,7 @@
#include <kernel/PIT.h> #include <kernel/PIT.h>
#include <kernel/RTC.h> #include <kernel/RTC.h>
#include <kernel/Shell.h> #include <kernel/Shell.h>
#include <kernel/Serial.h>
#include <kernel/tty.h> #include <kernel/tty.h>
namespace Kernel namespace Kernel
@ -172,6 +173,47 @@ namespace Kernel
kprintln("unrecognized command '{}'", arguments.Front()); 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) void Shell::KeyEventCallback(Keyboard::KeyEvent event)
{ {
if (!event.pressed) if (!event.pressed)
@ -184,7 +226,10 @@ namespace Kernel
if (!m_buffer.Empty()) if (!m_buffer.Empty())
{ {
kprint("\b \b", 3); 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; break;
} }
@ -200,21 +245,19 @@ namespace Kernel
} }
case Keyboard::Key::Tab: case Keyboard::Key::Tab:
event.key = Keyboard::Key::Space; break;
// fall through
default: default:
{ {
char ascii = Keyboard::key_event_to_ascii(event); const char* utf8 = Keyboard::key_event_to_utf8(event);
if (ascii) if (utf8)
{ {
kprint("{}", ascii); kprint("{}", utf8);
m_buffer.PushBack(ascii); m_buffer.Append(utf8);
} }
break; break;
} }
} }
} }
} }