cowos/kernel/drivers/video/framebuffer.c

285 lines
7.6 KiB
C

#include <drivers/video/framebuffer.h>
#include <klibc/string.h>
#include <stddef.h>
#include <stdarg.h>
#include <limine_requests.h>
#include <drivers/video/font8x16.h>
static fb_console_t console;
void fb_init(void)
{
if (framebuffer_request.response == NULL ||
framebuffer_request.response->framebuffer_count < 1) {
return;
}
console.fb = framebuffer_request.response->framebuffers[0];
console.fg_color = 0xFFFFFF; // White
console.bg_color = 0x000000; // Black
console.cursor_x = 0;
console.cursor_y = 0;
console.cols = console.fb->width / FB_CHAR_WIDTH;
console.rows = console.fb->height / FB_CHAR_HEIGHT;
fb_clear();
}
void fb_clear(void)
{
if (!console.fb) return;
uint32_t *fb = (uint32_t *)console.fb->address;
size_t pixels = (console.fb->pitch / 4) * console.fb->height;
for (size_t i = 0; i < pixels; i++) {
fb[i] = console.bg_color;
}
console.cursor_x = 0;
console.cursor_y = 0;
}
void fb_draw_rect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t color)
{
if (!console.fb) return;
uint32_t *fb = (uint32_t *)console.fb->address;
uint32_t pitch_pixels = console.fb->pitch / 4;
for (uint32_t row = y; row < y + h && row < console.fb->height; row++) {
for (uint32_t col = x; col < x + w && col < console.fb->width; col++) {
fb[row * pitch_pixels + col] = color;
}
}
}
void fb_set_color(uint32_t fg, uint32_t bg)
{
console.fg_color = fg;
console.bg_color = bg;
}
static void draw_char(uint32_t x, uint32_t y, char c, uint32_t fg_color, uint32_t bg_color)
{
if (!console.fb) return;
uint32_t *fb = (uint32_t *)console.fb->address;
uint32_t pitch_pixels = console.fb->pitch / 4;
// Get the font data for this character
const uint8_t *char_data = &IBM_VGA_8x16[c * 16];
// Draw each row of the character
for (uint32_t row = 0; row < FB_CHAR_HEIGHT; row++) {
uint8_t font_row = char_data[row];
// Draw each pixel in the row
for (uint32_t col = 0; col < FB_CHAR_WIDTH; col++) {
uint32_t px = x + col;
uint32_t py = y + row;
// Check bounds
if (px >= console.fb->width || py >= console.fb->height) continue;
// Check if this pixel should be set (bit test)
if (font_row & (0x80 >> col)) {
fb[py * pitch_pixels + px] = fg_color;
} else {
fb[py * pitch_pixels + px] = bg_color;
}
}
}
}
static void scroll_up(void)
{
if (!console.fb) return;
uint32_t *fb = (uint32_t *)console.fb->address;
uint32_t pitch_pixels = console.fb->pitch / 4;
// Move all lines up by one character height
for (uint32_t y = 0; y < console.fb->height - FB_CHAR_HEIGHT; y++) {
for (uint32_t x = 0; x < console.fb->width; x++) {
fb[y * pitch_pixels + x] = fb[(y + FB_CHAR_HEIGHT) * pitch_pixels + x];
}
}
// Clear the last line
for (uint32_t y = console.fb->height - FB_CHAR_HEIGHT; y < console.fb->height; y++) {
for (uint32_t x = 0; x < console.fb->width; x++) {
fb[y * pitch_pixels + x] = console.bg_color;
}
}
}
void fb_putchar(char c)
{
if (!console.fb) return;
// Handle special characters
switch (c) {
case '\n':
console.cursor_x = 0;
console.cursor_y++;
break;
case '\r':
console.cursor_x = 0;
break;
case '\t':
console.cursor_x = (console.cursor_x + 8) & ~7;
break;
case '\b':
if (console.cursor_x > 0) {
console.cursor_x--;
draw_char(console.cursor_x * FB_CHAR_WIDTH,
console.cursor_y * FB_CHAR_HEIGHT,
' ', console.fg_color, console.bg_color);
}
break;
default:
// Draw the character
draw_char(console.cursor_x * FB_CHAR_WIDTH,
console.cursor_y * FB_CHAR_HEIGHT,
c, console.fg_color, console.bg_color);
console.cursor_x++;
break;
}
// Handle line wrapping
if (console.cursor_x >= console.cols) {
console.cursor_x = 0;
console.cursor_y++;
}
// Handle scrolling
if (console.cursor_y >= console.rows) {
scroll_up();
console.cursor_y = console.rows - 1;
}
}
void fb_puts(const char *str)
{
while (*str) {
fb_putchar(*str++);
}
}
static void print_number(unsigned long num, int base, int width, char pad)
{
char digits[] = "0123456789ABCDEF";
char buffer[32];
int pos = 0;
// Convert number to string (reversed)
if (num == 0) {
buffer[pos++] = '0';
} else {
while (num > 0) {
buffer[pos++] = digits[num % base];
num /= base;
}
}
// Add padding if needed
while (pos < width) {
buffer[pos++] = pad;
}
// Print reversed
while (pos > 0) {
fb_putchar(buffer[--pos]);
}
}
static void print_signed(long num, int width, char pad)
{
if (num < 0) {
fb_putchar('-');
print_number(-num, 10, width, pad);
} else {
print_number(num, 10, width, pad);
}
}
void fb_printf(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
while (*fmt) {
if (*fmt == '%') {
fmt++;
// Handle width and padding
char pad = ' ';
int width = 0;
if (*fmt == '0') {
pad = '0';
fmt++;
}
while (*fmt >= '0' && *fmt <= '9') {
width = width * 10 + (*fmt - '0');
fmt++;
}
// Handle format specifiers
switch (*fmt) {
case 'd':
case 'i':
print_signed(va_arg(args, int), width, pad);
break;
case 'u':
print_number(va_arg(args, unsigned int), 10, width, pad);
break;
case 'x':
print_number(va_arg(args, unsigned int), 16, width, pad);
break;
case 'X':
print_number(va_arg(args, unsigned int), 16, width, pad);
break;
case 'p':
fb_puts("0x");
print_number((unsigned long)va_arg(args, void*), 16, 16, '0');
break;
case 'c':
fb_putchar(va_arg(args, int));
break;
case 's':
{
const char *str = va_arg(args, const char*);
if (str) {
fb_puts(str);
} else {
fb_puts("(null)");
}
}
break;
case '%':
fb_putchar('%');
break;
default:
fb_putchar('%');
fb_putchar(*fmt);
break;
}
} else {
fb_putchar(*fmt);
}
fmt++;
}
va_end(args);
}