185 lines
4.3 KiB
C++

#include "os/tty.hpp"
#include <cstring>
#include <vector>
#include "drivers/ps2.hpp"
#include "libs/psf.hpp"
#include "os/draw.hpp"
#include "os/resources/lat9-08.psf.hpp"
namespace tty
{
namespace
{
size_t gTerminalRow = 0;
size_t gTerminalColumn = 0;
size_t gTerminalWidth = 0;
size_t gTerminalHeight = 0;
VgaDoubleColor gTerminalColor = VgaDoubleColor();
// uint16_t* gTerminalBuffer = reinterpret_cast<uint16_t*>(0xB8000);
std::vector<VgaCharacter> gTerminalBuffer;
psf::Font gTerminalFont;
std::string gReadBuffer;
constexpr uint8_t vgaEntryColor(VgaDoubleColor color)
{
return static_cast<uint8_t>(color.foreground) | static_cast<uint8_t>(color.background) << 4;
}
constexpr uint16_t vgaEntry(const unsigned char chr, const VgaDoubleColor color)
{
return static_cast<uint16_t>(chr) | static_cast<uint16_t>(vgaEntryColor(color) << 8);
}
void newline()
{
gTerminalColumn = 0;
if (gTerminalRow < gTerminalHeight - 1)
{
++gTerminalRow;
}
else
{
draw::scrollBy(0, static_cast<int>(gTerminalFont.getGlyphHeight()) + 1);
std::memmove(&gTerminalBuffer[0], &gTerminalBuffer[gTerminalWidth], (gTerminalHeight - 1) * gTerminalWidth * sizeof(VgaCharacter));
for (std::size_t posX = 0; posX < gTerminalWidth; ++posX) {
putEntryAt('\0', gTerminalColor, posX, gTerminalHeight - 1);
}
}
}
}
void initialize() noexcept
{
setPSFFont(&LAT9_08[0], LAT9_08_SIZE);
}
void setColor(VgaDoubleColor color) noexcept
{
gTerminalColor = color;
}
const VgaDoubleColor& getColor() noexcept
{
return gTerminalColor;
}
void putEntryAt(char chr, VgaDoubleColor color, size_t posX, size_t posY) noexcept
{
draw::character(
/* posX = */ (gTerminalFont.getGlyphWidth() + 1) * posX,
/* posY = */ (gTerminalFont.getGlyphHeight() + 1) * posY,
/* font = */ gTerminalFont,
/* chr = */ chr ? chr : ' ',
/* fgColor = */ draw::VGA_PIXELS[static_cast<uint8_t>(color.foreground)],
/* bgColor = */ draw::VGA_PIXELS[static_cast<uint8_t>(color.background)]
);
gTerminalBuffer[posY * gTerminalHeight + posX] = {
.character = chr,
.color = color
};
}
VgaCharacter getEntryAt(size_t posX, size_t posY) noexcept
{
return gTerminalBuffer[posY * gTerminalHeight + posX];
}
void putChar(char chr) noexcept
{
putChar(chr, gTerminalColor);
}
void putChar(char chr, VgaDoubleColor color) noexcept
{
if (chr == '\n')
{
newline();
return;
}
putEntryAt(chr, color, gTerminalColumn, gTerminalRow);
if (++gTerminalColumn == gTerminalWidth)
{
newline();
}
}
void deleteChar() noexcept
{
if (gTerminalColumn > 0)
{
--gTerminalColumn;
putEntryAt(' ', gTerminalColor, gTerminalColumn, gTerminalRow);
// TODO: store the length of each line so we can return to the previous one
}
}
void write(const char* data, size_t size) noexcept
{
for (size_t idx = 0; idx < size; idx++)
{
putChar(data[idx]);
}
}
void write(const char* data) noexcept
{
write(data, std::strlen(data));
}
unsigned rowLength(unsigned row)
{
unsigned length = 0;
for (; length < gTerminalWidth; ++length)
{
if (getEntryAt(length, row).character == '\0') {
break;
}
}
return length;
}
const std::string& readLine() noexcept
{
gReadBuffer.clear();
while(true)
{
char chr = '\0';
putChar(' ', gTerminalColor.swapped());
while (!baos::ps2::readChar(chr)); // TODO: arrow key navigation and stuff
deleteChar();
if (chr == '\b')
{
if (!gReadBuffer.empty())
{
gReadBuffer.pop_back();
deleteChar();
}
continue;
}
gReadBuffer += chr;
putChar(chr);
if (chr == '\n') {
return gReadBuffer;
}
}
}
bool setPSFFont(const void* data, size_t length) noexcept
{
psf::Font newFont;
if (!psf::Font::create(data, length, newFont)) {
return false;
}
gTerminalFont = newFont;
gTerminalWidth = draw::getPrimaryFramebuffer().getWidth() / (gTerminalFont.getGlyphWidth() + 1);
gTerminalHeight = draw::getPrimaryFramebuffer().getHeight() / (gTerminalFont.getGlyphHeight() + 1);
gTerminalBuffer.resize(gTerminalWidth * gTerminalHeight);
return true;
}
}