Kernel/core/console.cpp

349 lines
10 KiB
C++

/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <kcon.hpp>
#include <memory.hpp>
#include <stropts.h>
#include <string.h>
#include <ini.h>
#include "../kernel.h"
namespace KernelConsole
{
int TermColors[] = {
[TerminalColor::BLACK] = 0x000000,
[TerminalColor::RED] = 0xAA0000,
[TerminalColor::GREEN] = 0x00AA00,
[TerminalColor::YELLOW] = 0xAA5500,
[TerminalColor::BLUE] = 0x0000AA,
[TerminalColor::MAGENTA] = 0xAA00AA,
[TerminalColor::CYAN] = 0x00AAAA,
[TerminalColor::GREY] = 0xAAAAAA,
};
int TermBrightColors[] = {
[TerminalColor::BLACK] = 0x858585,
[TerminalColor::RED] = 0xFF5555,
[TerminalColor::GREEN] = 0x55FF55,
[TerminalColor::YELLOW] = 0xFFFF55,
[TerminalColor::BLUE] = 0x5555FF,
[TerminalColor::MAGENTA] = 0xFF55FF,
[TerminalColor::CYAN] = 0x55FFFF,
[TerminalColor::GREY] = 0xFFFFFF,
};
__no_sanitize("undefined") char FontRenderer::Paint(long CellX, long CellY, char Char, uint32_t Foreground, uint32_t Background)
{
uint64_t x = CellX * CurrentFont->GetInfo().Width;
uint64_t y = CellY * CurrentFont->GetInfo().Height;
switch (CurrentFont->GetInfo().Type)
{
case Video::FontType::PCScreenFont1:
{
uint32_t *PixelPtr = (uint32_t *)Display->GetBuffer;
char *FontPtr = (char *)CurrentFont->GetInfo().PSF1Font->GlyphBuffer + (Char * CurrentFont->GetInfo().PSF1Font->Header->charsize);
for (uint64_t Y = 0; Y < 16; Y++)
{
for (uint64_t X = 0; X < 8; X++)
{
if ((*FontPtr & (0b10000000 >> X)) > 0)
*(unsigned int *)(PixelPtr + (x + X) + ((y + Y) * Display->GetWidth)) = Foreground;
else
*(unsigned int *)(PixelPtr + (x + X) + ((y + Y) * Display->GetWidth)) = Background;
}
FontPtr++;
}
break;
}
case Video::FontType::PCScreenFont2:
{
Video::FontInfo fInfo = CurrentFont->GetInfo();
int BytesPerLine = (fInfo.PSF2Font->Header->width + 7) / 8;
char *FontAddress = (char *)fInfo.StartAddress;
uint32_t FontHeaderSize = fInfo.PSF2Font->Header->headersize;
uint32_t FontCharSize = fInfo.PSF2Font->Header->charsize;
uint32_t FontLength = fInfo.PSF2Font->Header->length;
char *FontPtr = FontAddress + FontHeaderSize + (Char > 0 && (uint32_t)Char < FontLength ? Char : 0) * FontCharSize;
uint32_t FontHdrWidth = fInfo.PSF2Font->Header->width;
uint32_t FontHdrHeight = fInfo.PSF2Font->Header->height;
for (uint32_t Y = 0; Y < FontHdrHeight; Y++)
{
for (uint32_t X = 0; X < FontHdrWidth; X++)
{
void *FramebufferAddress = (void *)((uintptr_t)Display->GetBuffer +
((y + Y) * Display->GetWidth + (x + X)) *
(Display->GetFramebufferStruct().BitsPerPixel / 8));
if ((*FontPtr & (0b10000000 >> (X % 8))) > 0)
*(uint32_t *)FramebufferAddress = Foreground;
else
*(uint32_t *)FramebufferAddress = Background;
}
FontPtr += BytesPerLine;
}
break;
}
default:
warn("Unsupported font type");
break;
}
return Char;
}
FontRenderer Renderer;
void paint_callback(TerminalCell *cell, long x, long y)
{
if (cell->attr.Bright)
Renderer.Paint(x, y, cell->c, TermBrightColors[cell->attr.Foreground], TermColors[cell->attr.Background]);
else
Renderer.Paint(x, y, cell->c, TermColors[cell->attr.Foreground], TermColors[cell->attr.Background]);
}
void cursor_callback(TerminalCursor *cur)
{
Renderer.Cursor = {cur->X, cur->Y};
}
VirtualTerminal *Terminals[16] = {nullptr};
std::atomic<VirtualTerminal *> CurrentTerminal = nullptr;
bool SetTheme(std::string Theme)
{
FileNode *rn = fs->GetByPath("/etc/term", thisProcess->Info.RootNode);
if (rn == nullptr)
return false;
kstat st{};
rn->Stat(&st);
char *sh = new char[st.Size];
rn->Read(sh, st.Size, 0);
ini_t *ini = ini_load(sh, NULL);
int themeSection, c0, c1, c2, c3, c4, c5, c6, c7, colorsIdx;
const char *colors[8];
debug("Loading terminal theme: \"%s\"", Theme.c_str());
themeSection = ini_find_section(ini, Theme.c_str(), NULL);
if (themeSection == INI_NOT_FOUND)
{
ini_destroy(ini);
delete[] sh;
return false;
}
auto getColorComponent = [](const char *str, int &index) -> int
{
int value = 0;
while (str[index] >= '0' && str[index] <= '9')
{
value = value * 10 + (str[index] - '0');
++index;
}
return value;
};
auto parseColor = [getColorComponent](const char *colorStr) -> unsigned int
{
int index = 0;
int r = getColorComponent(colorStr, index);
if (colorStr[index] == ',')
++index;
int g = getColorComponent(colorStr, index);
if (colorStr[index] == ',')
++index;
int b = getColorComponent(colorStr, index);
return (r << 16) | (g << 8) | b;
};
c0 = ini_find_property(ini, themeSection, "color0", NULL);
c1 = ini_find_property(ini, themeSection, "color1", NULL);
c2 = ini_find_property(ini, themeSection, "color2", NULL);
c3 = ini_find_property(ini, themeSection, "color3", NULL);
c4 = ini_find_property(ini, themeSection, "color4", NULL);
c5 = ini_find_property(ini, themeSection, "color5", NULL);
c6 = ini_find_property(ini, themeSection, "color6", NULL);
c7 = ini_find_property(ini, themeSection, "color7", NULL);
colors[0] = ini_property_value(ini, themeSection, c0);
colors[1] = ini_property_value(ini, themeSection, c1);
colors[2] = ini_property_value(ini, themeSection, c2);
colors[3] = ini_property_value(ini, themeSection, c3);
colors[4] = ini_property_value(ini, themeSection, c4);
colors[5] = ini_property_value(ini, themeSection, c5);
colors[6] = ini_property_value(ini, themeSection, c6);
colors[7] = ini_property_value(ini, themeSection, c7);
colorsIdx = 0;
for (auto color : colors)
{
colorsIdx++;
if (color == 0)
continue;
char *delimiterPos = strchr(color, ':');
if (delimiterPos == NULL)
continue;
char colorStr[20], colorBrightStr[20];
strncpy(colorStr, color, delimiterPos - color);
colorStr[delimiterPos - color] = '\0';
strcpy(colorBrightStr, delimiterPos + 1);
TermColors[colorsIdx - 1] = parseColor(colorStr);
TermBrightColors[colorsIdx - 1] = parseColor(colorBrightStr);
}
ini_destroy(ini);
delete[] sh;
return true;
}
#ifdef DEBUG
void __test_themes()
{
printf("\x1b[H\x1b[2J");
auto testTheme = [](const char *theme)
{
KernelConsole::SetTheme("vga");
printf("== Theme: \"%s\" ==\n", theme);
KernelConsole::SetTheme(theme);
const char *txtColors[] = {
"30", "31", "32", "33", "34", "35", "36", "37", "39"};
const char *bgColors[] = {
"40", "41", "42", "43", "44", "45", "46", "47", "49"};
const int numTxtColors = sizeof(txtColors) / sizeof(txtColors[0]);
const int numBgColors = sizeof(bgColors) / sizeof(bgColors[0]);
const char *msg = "H";
for (int i = 0; i < numTxtColors; i++)
for (int j = 0; j < numBgColors; j++)
printf("\x1b[%sm\x1b[%sm%s\x1b[0m", txtColors[i], bgColors[j], msg);
for (int i = 0; i < numTxtColors; i++)
for (int j = 0; j < numBgColors; j++)
printf("\x1b[1;%sm\x1b[1;%sm%s\x1b[0m", txtColors[i], bgColors[j], msg);
KernelConsole::SetTheme("vga");
printf("\n");
};
testTheme("vga");
testTheme("breeze");
testTheme("coolbreeze");
testTheme("softlight");
testTheme("calmsea");
testTheme("warmember");
// CPU::Stop();
}
#endif
void EarlyInit()
{
Renderer.CurrentFont = new Video::Font(&_binary_files_tamsyn_font_1_11_Tamsyn7x14r_psf_start,
&_binary_files_tamsyn_font_1_11_Tamsyn7x14r_psf_end,
Video::FontType::PCScreenFont2);
size_t Rows = Display->GetWidth / Renderer.CurrentFont->GetInfo().Width;
size_t Cols = Display->GetHeight / Renderer.CurrentFont->GetInfo().Height;
debug("Terminal size: %ux%u", Rows, Cols);
Terminals[0] = new VirtualTerminal(Rows, Cols, Display->GetWidth, Display->GetHeight, paint_callback, cursor_callback);
Terminals[0]->Clear(0, 0, Rows, Cols - 1);
CurrentTerminal.store(Terminals[0], std::memory_order_release);
}
void LateInit()
{
FileNode *rn = fs->GetByPath("/etc/term", thisProcess->Info.RootNode);
if (rn == nullptr)
return;
kstat st{};
rn->Stat(&st);
char *sh = new char[st.Size];
rn->Read(sh, st.Size, 0);
ini_t *ini = ini_load(sh, NULL);
int general = ini_find_section(ini, "general", NULL);
int cursor = ini_find_section(ini, "cursor", NULL);
assert(general != INI_NOT_FOUND && cursor != INI_NOT_FOUND);
int themeIndex = ini_find_property(ini, general, "theme", NULL);
assert(themeIndex != INI_NOT_FOUND);
int cursorColor = ini_find_property(ini, cursor, "color", NULL);
int cursorBlink = ini_find_property(ini, cursor, "blink", NULL);
assert(cursorColor != INI_NOT_FOUND && cursorBlink != INI_NOT_FOUND);
const char *colorThemeStr = ini_property_value(ini, general, themeIndex);
const char *cursorColorStr = ini_property_value(ini, cursor, cursorColor);
const char *cursorBlinkStr = ini_property_value(ini, cursor, cursorBlink);
debug("colorThemeStr=%s", colorThemeStr);
debug("cursorColorStr=%s", cursorColorStr);
debug("cursorBlinkStr=%s", cursorBlinkStr);
auto getColorComponent = [](const char *str, int &index) -> int
{
int value = 0;
while (str[index] >= '0' && str[index] <= '9')
{
value = value * 10 + (str[index] - '0');
++index;
}
return value;
};
auto parseColor = [getColorComponent](const char *colorStr) -> unsigned int
{
int index = 0;
int r = getColorComponent(colorStr, index);
if (colorStr[index] == ',')
++index;
int g = getColorComponent(colorStr, index);
if (colorStr[index] == ',')
++index;
int b = getColorComponent(colorStr, index);
return (r << 16) | (g << 8) | b;
};
if (colorThemeStr != 0)
SetTheme(colorThemeStr);
if (cursorBlinkStr != 0 && strncmp(cursorBlinkStr, "true", 4) == 0)
{
uint32_t blinkColor = 0xFFFFFF;
if (cursorColorStr != 0)
blinkColor = parseColor(cursorColorStr);
fixme("cursor blink with colors %X", blinkColor);
}
ini_destroy(ini);
delete[] sh;
#ifdef DEBUG
// __test_themes();
#endif
}
}