215 lines
4.5 KiB
C
215 lines
4.5 KiB
C
/*
|
|
* @author xamidev <xamidev@riseup.net>
|
|
* @brief Framebuffer-based terminal driver
|
|
* @license GPL-3.0-only
|
|
*/
|
|
|
|
// Terminal output
|
|
/*
|
|
There are a couple of bugs here and there but for now I don't care too much
|
|
because this shitty implementation will be replaced one day by Flanterm
|
|
(once memory management is okay: paging & kernel malloc)
|
|
*/
|
|
|
|
#include <limine.h>
|
|
#include <stddef.h>
|
|
#include <kernel.h>
|
|
#include "term.h"
|
|
#include "mem/misc/utils.h"
|
|
#include "config.h"
|
|
|
|
extern struct boot_context boot_ctx;
|
|
|
|
// Importing the PSF object file
|
|
extern unsigned char _binary_zap_light16_psf_start[];
|
|
extern unsigned char _binary_zap_light16_psf_end[];
|
|
|
|
PSF1_Header* font = (PSF1_Header*)_binary_zap_light16_psf_start;
|
|
uint8_t* glyphs = _binary_zap_light16_psf_start + sizeof(PSF1_Header);
|
|
|
|
#define FONT_WIDTH 8
|
|
#define FONT_HEIGHT font->characterSize
|
|
|
|
|
|
|
|
// Character cursor
|
|
typedef struct
|
|
{
|
|
size_t x;
|
|
size_t y;
|
|
} Cursor;
|
|
|
|
static Cursor cursor = {0, 0};
|
|
|
|
static uint8_t* fb;
|
|
static struct limine_framebuffer* framebuffer;
|
|
|
|
uint8_t lines_length[TERM_HISTORY_MAX_LINES];
|
|
|
|
static inline size_t term_max_cols(void)
|
|
{
|
|
return framebuffer->width / FONT_WIDTH;
|
|
}
|
|
|
|
static inline size_t term_max_lines(void)
|
|
{
|
|
return framebuffer->height / FONT_HEIGHT;
|
|
}
|
|
|
|
int term_init()
|
|
{
|
|
// Get framebuffer address from Limine struct
|
|
|
|
if (!boot_ctx.fb)
|
|
{
|
|
return -ENOMEM;
|
|
}
|
|
|
|
framebuffer = boot_ctx.fb;
|
|
fb = (uint8_t*)framebuffer->address;
|
|
|
|
memset(lines_length, 0, sizeof(lines_length));
|
|
|
|
DEBUG("terminal initialized, fb=0x%p (width=%u height=%u pitch=%u bpp=%u)", fb, framebuffer->width, framebuffer->height, framebuffer->pitch, framebuffer->bpp);
|
|
return 0;
|
|
}
|
|
|
|
// These are marked "static" because we don't wanna expose them all around
|
|
// AKA they should just be seen here (kind of like private functions in cpp)
|
|
static inline void putpixel(size_t x, size_t y, uint32_t color)
|
|
{
|
|
// Guard so we don't write past fb boundaries
|
|
if (x >= framebuffer->width || y >= framebuffer->height) return;
|
|
// Depth isn't part of limine_framebuffer attributes so it will be 4
|
|
size_t pos = x*4 + y*framebuffer->pitch;
|
|
fb[pos] = color & 255; // blue channel
|
|
fb[pos+1] = (color >> 8) & 255; // green
|
|
fb[pos+2] = (color >> 16) & 255; // blue
|
|
}
|
|
|
|
static void draw_char(char c, size_t px, size_t py, uint32_t fg, uint32_t bg)
|
|
{
|
|
// So we cannot write past fb
|
|
if (px+FONT_WIDTH > framebuffer->width || py+FONT_HEIGHT > framebuffer->height) return;
|
|
|
|
uint8_t* glyph = glyphs + ((unsigned char)c * FONT_HEIGHT);
|
|
|
|
for (size_t y=0; y<FONT_HEIGHT; y++)
|
|
{
|
|
uint8_t row = glyph[y];
|
|
for (size_t x=0; x<FONT_WIDTH; x++)
|
|
{
|
|
uint32_t color = (row & (0x80 >> x)) ? fg : bg;
|
|
putpixel(px+x, py+y, color);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void erase_char(size_t px, size_t py)
|
|
{
|
|
if (px+FONT_WIDTH > framebuffer->width || py+FONT_HEIGHT > framebuffer->height) return;
|
|
|
|
for (size_t y=0; y<FONT_HEIGHT; y++)
|
|
{
|
|
for (size_t x=0; x<FONT_WIDTH; x++)
|
|
{
|
|
// Black
|
|
putpixel(px+x, py+y, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void term_scroll()
|
|
{
|
|
// Erase first text line
|
|
memset(fb, 255, FONT_HEIGHT*framebuffer->pitch);
|
|
|
|
// Move whole framebuffer up by one text line
|
|
memmove(fb, fb+(FONT_HEIGHT*framebuffer->pitch), (framebuffer->height-FONT_HEIGHT)*framebuffer->pitch);
|
|
|
|
// Clear last text line
|
|
size_t clear_start = (framebuffer->height - FONT_HEIGHT) * framebuffer->pitch;
|
|
memset(fb + clear_start, 255, FONT_HEIGHT * framebuffer->pitch);
|
|
|
|
// Shift line lengths by 1 (for backspace handling)
|
|
size_t max_lines = term_max_lines();
|
|
for (size_t i = 1; i < max_lines; i++)
|
|
{
|
|
lines_length[i - 1] = lines_length[i];
|
|
}
|
|
lines_length[max_lines - 1] = 0;
|
|
}
|
|
|
|
void putchar(char c)
|
|
{
|
|
const size_t max_cols = term_max_cols();
|
|
const size_t max_lines = term_max_lines();
|
|
|
|
if (c == '\n') {
|
|
lines_length[cursor.y] = cursor.x;
|
|
cursor.x = 0;
|
|
|
|
if (cursor.y + 1 >= max_lines)
|
|
{
|
|
term_scroll();
|
|
}
|
|
else
|
|
{
|
|
cursor.y++;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (c == '\b')
|
|
{
|
|
if (cursor.x > 0)
|
|
{
|
|
cursor.x--;
|
|
}
|
|
else if (cursor.y > 0)
|
|
{
|
|
cursor.y--;
|
|
cursor.x = lines_length[cursor.y];
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
|
|
erase_char(cursor.x * FONT_WIDTH, cursor.y * FONT_HEIGHT);
|
|
return;
|
|
}
|
|
|
|
if (cursor.x >= max_cols)
|
|
{
|
|
cursor.x = 0;
|
|
if (cursor.y + 1 >= max_lines)
|
|
{
|
|
term_scroll();
|
|
}
|
|
else
|
|
{
|
|
cursor.y++;
|
|
}
|
|
}
|
|
|
|
draw_char(c, cursor.x * FONT_WIDTH, cursor.y * FONT_HEIGHT, WHITE, BLACK);
|
|
cursor.x++;
|
|
}
|
|
|
|
// Overhead that could be avoided, right? (for printf)
|
|
void _putchar(char character)
|
|
{
|
|
putchar(character);
|
|
}
|
|
|
|
// Debug-printing
|
|
void kputs(const char* str)
|
|
{
|
|
size_t i=0;
|
|
while (str[i] != 0)
|
|
{
|
|
putchar(str[i]);
|
|
i++;
|
|
}
|
|
} |