Files
cleonos/clks/kernel/hal/video/framebuffer.c

316 lines
7.7 KiB
C
Raw Normal View History

2026-04-09 21:47:13 +08:00
#include <clks/framebuffer.h>
2026-04-11 16:46:10 +08:00
#include <clks/string.h>
2026-04-09 21:47:13 +08:00
#include <clks/types.h>
2026-04-11 16:11:16 +08:00
#include "psf_font.h"
2026-04-09 21:47:13 +08:00
struct clks_fb_state {
volatile u8 *address;
struct clks_framebuffer_info info;
2026-04-11 16:11:16 +08:00
const struct clks_psf_font *font;
struct clks_psf_font external_font;
clks_bool external_font_active;
u32 glyph_width;
u32 glyph_height;
2026-04-09 21:47:13 +08:00
clks_bool ready;
};
static struct clks_fb_state clks_fb = {
.address = CLKS_NULL,
.info = {0, 0, 0, 0},
2026-04-11 16:11:16 +08:00
.font = CLKS_NULL,
2026-04-11 22:27:54 +08:00
.external_font = {0, 0, 0, 0, 0, CLKS_NULL},
2026-04-11 16:11:16 +08:00
.external_font_active = CLKS_FALSE,
.glyph_width = 8U,
.glyph_height = 8U,
2026-04-09 21:47:13 +08:00
.ready = CLKS_FALSE,
};
2026-04-11 16:11:16 +08:00
static void clks_fb_apply_font(const struct clks_psf_font *font) {
clks_fb.font = font;
clks_fb.glyph_width = 8U;
clks_fb.glyph_height = 8U;
if (font != CLKS_NULL) {
if (font->width != 0U) {
clks_fb.glyph_width = font->width;
}
if (font->height != 0U) {
clks_fb.glyph_height = font->height;
}
}
}
2026-04-09 21:47:13 +08:00
static void clks_fb_put_pixel(u32 x, u32 y, u32 rgb) {
volatile u8 *row;
volatile u32 *pixel;
if (clks_fb.ready == CLKS_FALSE) {
return;
}
if (x >= clks_fb.info.width || y >= clks_fb.info.height) {
return;
}
if (clks_fb.info.bpp != 32) {
return;
}
row = clks_fb.address + ((usize)y * (usize)clks_fb.info.pitch);
2026-04-11 16:11:16 +08:00
pixel = (volatile u32 *)(row + ((usize)x * 4U));
2026-04-09 21:47:13 +08:00
*pixel = rgb;
}
void clks_fb_init(const struct limine_framebuffer *fb) {
if (fb == CLKS_NULL) {
clks_fb.ready = CLKS_FALSE;
return;
}
clks_fb.address = (volatile u8 *)fb->address;
clks_fb.info.width = (u32)fb->width;
clks_fb.info.height = (u32)fb->height;
clks_fb.info.pitch = (u32)fb->pitch;
clks_fb.info.bpp = fb->bpp;
2026-04-11 16:11:16 +08:00
clks_fb.external_font_active = CLKS_FALSE;
clks_fb_apply_font(clks_psf_default_font());
2026-04-09 21:47:13 +08:00
clks_fb.ready = CLKS_TRUE;
}
clks_bool clks_fb_ready(void) {
return clks_fb.ready;
}
struct clks_framebuffer_info clks_fb_info(void) {
return clks_fb.info;
}
2026-04-12 18:14:59 +08:00
void clks_fb_draw_pixel(u32 x, u32 y, u32 rgb) {
clks_fb_put_pixel(x, y, rgb);
}
clks_bool clks_fb_read_pixel(u32 x, u32 y, u32 *out_rgb) {
volatile u8 *row;
volatile u32 *pixel;
if (out_rgb == CLKS_NULL) {
return CLKS_FALSE;
}
if (clks_fb.ready == CLKS_FALSE) {
return CLKS_FALSE;
}
if (x >= clks_fb.info.width || y >= clks_fb.info.height) {
return CLKS_FALSE;
}
if (clks_fb.info.bpp != 32) {
return CLKS_FALSE;
}
row = clks_fb.address + ((usize)y * (usize)clks_fb.info.pitch);
pixel = (volatile u32 *)(row + ((usize)x * 4U));
*out_rgb = *pixel;
return CLKS_TRUE;
}
void clks_fb_fill_rect(u32 x, u32 y, u32 width, u32 height, u32 rgb) {
u32 px;
u32 py;
u32 end_x;
u32 end_y;
u64 end_x64;
u64 end_y64;
2026-04-09 21:47:13 +08:00
if (clks_fb.ready == CLKS_FALSE) {
return;
}
2026-04-12 18:14:59 +08:00
if (width == 0U || height == 0U) {
return;
}
if (x >= clks_fb.info.width || y >= clks_fb.info.height) {
return;
}
end_x64 = (u64)x + (u64)width;
end_y64 = (u64)y + (u64)height;
end_x = (end_x64 > (u64)clks_fb.info.width) ? clks_fb.info.width : (u32)end_x64;
end_y = (end_y64 > (u64)clks_fb.info.height) ? clks_fb.info.height : (u32)end_y64;
for (py = y; py < end_y; py++) {
for (px = x; px < end_x; px++) {
clks_fb_put_pixel(px, py, rgb);
2026-04-09 21:47:13 +08:00
}
}
}
2026-04-12 18:14:59 +08:00
void clks_fb_clear(u32 rgb) {
clks_fb_fill_rect(0U, 0U, clks_fb.info.width, clks_fb.info.height, rgb);
}
2026-04-11 16:46:10 +08:00
void clks_fb_scroll_up(u32 pixel_rows, u32 fill_rgb) {
usize row_bytes;
usize move_bytes;
u32 y;
if (clks_fb.ready == CLKS_FALSE) {
return;
}
if (clks_fb.info.bpp != 32) {
return;
}
if (pixel_rows == 0U) {
return;
}
if (pixel_rows >= clks_fb.info.height) {
clks_fb_clear(fill_rgb);
return;
}
row_bytes = (usize)clks_fb.info.pitch;
move_bytes = (usize)(clks_fb.info.height - pixel_rows) * row_bytes;
clks_memmove((void *)clks_fb.address, (const void *)(clks_fb.address + ((usize)pixel_rows * row_bytes)),
move_bytes);
2026-04-11 16:46:10 +08:00
for (y = clks_fb.info.height - pixel_rows; y < clks_fb.info.height; y++) {
volatile u32 *row_ptr = (volatile u32 *)(clks_fb.address + ((usize)y * row_bytes));
u32 x;
2026-04-11 16:46:10 +08:00
for (x = 0U; x < clks_fb.info.width; x++) {
row_ptr[x] = fill_rgb;
2026-04-11 16:46:10 +08:00
}
}
}
2026-04-14 21:13:30 +08:00
void clks_fb_draw_char_styled(u32 x, u32 y, char ch, u32 fg_rgb, u32 bg_rgb, u32 style_flags) {
2026-04-09 21:47:13 +08:00
const u8 *glyph;
u32 row;
u32 col;
2026-04-11 16:11:16 +08:00
u32 cols;
u32 rows;
2026-04-11 22:27:54 +08:00
u32 row_stride;
u32 draw_cols;
u32 draw_rows;
2026-04-14 21:13:30 +08:00
clks_bool style_bold;
clks_bool style_underline;
u32 underline_row;
2026-04-09 21:47:13 +08:00
2026-04-11 16:11:16 +08:00
if (clks_fb.ready == CLKS_FALSE || clks_fb.font == CLKS_NULL) {
2026-04-09 21:47:13 +08:00
return;
}
if (clks_fb.info.bpp != 32) {
return;
}
if (x >= clks_fb.info.width || y >= clks_fb.info.height) {
return;
}
2026-04-11 16:11:16 +08:00
glyph = clks_psf_glyph(clks_fb.font, (u32)(u8)ch);
cols = clks_fb.glyph_width;
rows = clks_fb.glyph_height;
2026-04-09 21:47:13 +08:00
2026-04-11 16:11:16 +08:00
if (cols == 0U) {
cols = 8U;
}
if (rows == 0U) {
rows = 8U;
}
2026-04-11 22:27:54 +08:00
row_stride = clks_fb.font->bytes_per_row;
if (row_stride == 0U) {
row_stride = (cols + 7U) / 8U;
}
if (row_stride == 0U) {
return;
}
if (((usize)row_stride * (usize)rows) > (usize)clks_fb.font->bytes_per_glyph) {
return;
2026-04-11 16:11:16 +08:00
}
draw_cols = cols;
if (x + draw_cols > clks_fb.info.width) {
draw_cols = clks_fb.info.width - x;
}
draw_rows = rows;
if (y + draw_rows > clks_fb.info.height) {
draw_rows = clks_fb.info.height - y;
}
2026-04-14 21:13:30 +08:00
style_bold = ((style_flags & CLKS_FB_STYLE_BOLD) != 0U) ? CLKS_TRUE : CLKS_FALSE;
style_underline = ((style_flags & CLKS_FB_STYLE_UNDERLINE) != 0U) ? CLKS_TRUE : CLKS_FALSE;
underline_row = (rows > 1U) ? (rows - 2U) : 0U;
for (row = 0U; row < draw_rows; row++) {
2026-04-11 22:27:54 +08:00
const u8 *row_bits = glyph + ((usize)row * (usize)row_stride);
volatile u32 *dst_row =
(volatile u32 *)(clks_fb.address + ((usize)(y + row) * (usize)clks_fb.info.pitch) + ((usize)x * 4U));
2026-04-11 16:11:16 +08:00
for (col = 0U; col < draw_cols; col++) {
2026-04-11 22:27:54 +08:00
u8 bits = row_bits[col >> 3U];
u8 mask = (u8)(0x80U >> (col & 7U));
2026-04-14 21:13:30 +08:00
clks_bool pixel_on = ((bits & mask) != 0U) ? CLKS_TRUE : CLKS_FALSE;
if (style_bold == CLKS_TRUE && pixel_on == CLKS_FALSE && col > 0U) {
u32 left_col = col - 1U;
u8 left_bits = row_bits[left_col >> 3U];
u8 left_mask = (u8)(0x80U >> (left_col & 7U));
if ((left_bits & left_mask) != 0U) {
pixel_on = CLKS_TRUE;
}
}
if (style_underline == CLKS_TRUE && row == underline_row) {
pixel_on = CLKS_TRUE;
}
dst_row[col] = (pixel_on == CLKS_TRUE) ? fg_rgb : bg_rgb;
2026-04-09 21:47:13 +08:00
}
}
2026-04-11 16:11:16 +08:00
}
2026-04-14 21:13:30 +08:00
void clks_fb_draw_char(u32 x, u32 y, char ch, u32 fg_rgb, u32 bg_rgb) {
clks_fb_draw_char_styled(x, y, ch, fg_rgb, bg_rgb, 0U);
}
2026-04-11 16:11:16 +08:00
clks_bool clks_fb_load_psf_font(const void *blob, u64 blob_size) {
2026-04-11 22:27:54 +08:00
struct clks_psf_font parsed = {0, 0, 0, 0, 0, CLKS_NULL};
2026-04-11 16:11:16 +08:00
if (clks_psf_parse_font(blob, blob_size, &parsed) == CLKS_FALSE) {
return CLKS_FALSE;
}
clks_fb.external_font = parsed;
clks_fb.external_font_active = CLKS_TRUE;
clks_fb_apply_font(&clks_fb.external_font);
return CLKS_TRUE;
}
u32 clks_fb_cell_width(void) {
return clks_fb.glyph_width == 0U ? 8U : clks_fb.glyph_width;
}
u32 clks_fb_cell_height(void) {
return clks_fb.glyph_height == 0U ? 8U : clks_fb.glyph_height;
2026-04-11 22:27:54 +08:00
}