.ttf fonts (beta)

This commit is contained in:
boreddevnl
2026-03-03 20:34:50 +01:00
parent 8b3ca448ed
commit 3c7d36a50f
42 changed files with 5841 additions and 78 deletions

View File

@@ -25,8 +25,9 @@
#define DEFAULT_CMD_COLS 116
#define DEFAULT_CMD_ROWS 41
#define MAX_CMD_COLS 256
// Terminal cell dimensions — fixed to match the 8x8 bitmap font
#define LINE_HEIGHT 10
#define CHAR_WIDTH 8
#define CHAR_WIDTH 8
#define COLOR_RED 0xFFFF0000
@@ -1801,7 +1802,7 @@ static void cmd_paint(Window *win) {
if (r == cursor_row && c == cursor_col && win->focused) {
color = shell_config.bg_color;
}
draw_char(start_x + (c * CHAR_WIDTH), start_y + (r * LINE_HEIGHT), ch, color);
draw_char_bitmap(start_x + (c * CHAR_WIDTH), start_y + (r * LINE_HEIGHT), ch, color);
}
}
}

View File

@@ -3,6 +3,7 @@
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include "explorer.h"
#include "graphics.h"
#include "font_manager.h"
#include "fat32.h"
#include "disk.h"
#include "wm.h"
@@ -189,13 +190,13 @@ static void explorer_draw_icon_label(int x, int y, const char *label, uint32_t c
}
// Center in EXPLORER_ITEM_WIDTH
int l1_len = 0; while(line1[l1_len]) l1_len++;
int l1_w = l1_len * 8;
ttf_font_t *font = graphics_get_current_ttf();
int l1_w = font_manager_get_string_width(font, line1);
draw_string(x + (EXPLORER_ITEM_WIDTH - l1_w)/2, y + 56, line1, color);
if (line2[0]) {
int l2_len = 0; while(line2[l2_len]) l2_len++;
int l2_w = l2_len * 8;
int l2_w = font_manager_get_string_width(font, line2);
draw_string(x + (EXPLORER_ITEM_WIDTH - l2_w)/2, y + 66, line2, color);
}
}
@@ -926,7 +927,7 @@ static void explorer_paint(Window *win) {
draw_rect(offset_x, offset_y, win->w - 8, win->h - 28, COLOR_DARK_BG);
// Draw Drive Button (modern rounded style)
char drive_label[9]; // 8 chars + null
char drive_label[20];
// Extract drive from the window's current_path instead of using global current_drive
char current_drv = 'A';
if (state->current_path[0] && state->current_path[1] == ':') {
@@ -935,27 +936,48 @@ static void explorer_paint(Window *win) {
current_drv = state->current_path[0];
}
drive_label[0] = '[';
drive_label[1] = ' ';
drive_label[2] = current_drv;
drive_label[3] = ':';
drive_label[4] = ' ';
drive_label[5] = 'v';
drive_label[6] = ' ';
drive_label[7] = ']';
drive_label[8] = 0;
// Look up drive type
const char *type_str = "RAM";
Disk *drv = disk_get_by_letter(current_drv);
if (drv) {
switch (drv->type) {
case DISK_TYPE_RAM: type_str = "RAM"; break;
case DISK_TYPE_IDE: type_str = "IDE"; break;
case DISK_TYPE_SATA: type_str = "SATA"; break;
case DISK_TYPE_USB: type_str = "USB"; break;
default: type_str = "???"; break;
}
}
// Button at x+4, y+3, w=60 (rounded)
draw_rounded_rect_filled(win->x + 4, offset_y + 3, 60, 22, 5, COLOR_DARK_PANEL);
// Build label: "[ A:TYPE v ]"
int di = 0;
drive_label[di++] = '[';
drive_label[di++] = ' ';
drive_label[di++] = current_drv;
drive_label[di++] = ':';
const char *ts = type_str;
while (*ts) drive_label[di++] = *ts++;
drive_label[di++] = ' ';
drive_label[di++] = 'v';
drive_label[di++] = ' ';
drive_label[di++] = ']';
drive_label[di] = 0;
// Button at x+4, y+3, sized to fit label (rounded)
ttf_font_t *ttf_ = graphics_get_current_ttf();
int drive_label_w = ttf_ ? font_manager_get_string_width(ttf_, drive_label) + 16 : 80;
if (drive_label_w < 60) drive_label_w = 60;
draw_rounded_rect_filled(win->x + 4, offset_y + 3, drive_label_w, 22, 5, COLOR_DARK_PANEL);
draw_string(win->x + 12, offset_y + 8, drive_label, COLOR_DARK_TEXT);
// Draw path bar (shifted right, rounded, dark mode)
// Draw path bar (shifted right after drive button, rounded, dark mode)
int path_height = 22;
int path_x = offset_x + 64;
int path_w = win->w - 16 - 64;
int path_x = offset_x + drive_label_w + 8;
int path_w = win->w - 16 - drive_label_w - 8;
draw_rounded_rect_filled(path_x, offset_y + 3, path_w, path_height, 5, COLOR_DARK_PANEL);
draw_string(path_x + 6, offset_y + 8, "Path", COLOR_DARK_TEXT);
draw_string(path_x + 46, offset_y + 8, state->current_path, COLOR_DARK_TEXT);
draw_string(path_x + 6, offset_y + 8, "Path:", COLOR_DARK_TEXT);
int path_label_w = ttf_ ? font_manager_get_string_width(ttf_, "Path:") : 40;
draw_string(path_x + 6 + path_label_w + 6, offset_y + 8, state->current_path, COLOR_DARK_TEXT);
// Draw dropdown menu button (right-aligned, before back button, rounded)
int dropdown_btn_x = win->x + win->w - 90;
@@ -1073,7 +1095,19 @@ static void explorer_paint(Window *win) {
// Input field (rounded dark)
draw_rounded_rect_filled(dlg_x + 10, dlg_y + 35, 280, 20, 4, COLOR_DARK_BG);
draw_string(dlg_x + 15, dlg_y + 40, state->dialog_input, COLOR_WHITE);
draw_rect(dlg_x + 15 + state->dialog_input_cursor * 8, dlg_y + 39, 2, 12, COLOR_WHITE);
// Dynamic cursor — find start offset so text doesn't overflow the input box
{ int max_w = 265; /* input box is 280px wide with 15px left pad */
ttf_font_t *ttf_ = graphics_get_current_ttf();
int total_w = font_manager_get_string_width(ttf_, state->dialog_input);
int scroll_x = 0;
if (total_w > max_w) scroll_x = total_w - max_w;
char sub_[128]; int k_=0;
for(k_=0; k_<state->dialog_input_cursor && state->dialog_input[k_]; k_++) sub_[k_]=state->dialog_input[k_];
sub_[k_]=0;
int cx_ = font_manager_get_string_width(ttf_, sub_) - scroll_x;
if (cx_ < 0) cx_ = 0;
if (cx_ > max_w) cx_ = max_w;
draw_rect(dlg_x+15+cx_, dlg_y+39, 2, 12, COLOR_WHITE); }
// Buttons (rounded)
draw_rounded_rect_filled(dlg_x + 50, dlg_y + 65, 80, 25, 6, COLOR_DARK_BORDER);
@@ -1093,7 +1127,19 @@ static void explorer_paint(Window *win) {
// Input field (rounded dark)
draw_rounded_rect_filled(dlg_x + 10, dlg_y + 35, 280, 20, 4, COLOR_DARK_BG);
draw_string(dlg_x + 15, dlg_y + 40, state->dialog_input, COLOR_WHITE);
draw_rect(dlg_x + 15 + state->dialog_input_cursor * 8, dlg_y + 39, 2, 12, COLOR_WHITE);
// Dynamic cursor — find start offset so text doesn't overflow the input box
{ int max_w = 265; /* input box is 280px wide with 15px left pad */
ttf_font_t *ttf_ = graphics_get_current_ttf();
int total_w = font_manager_get_string_width(ttf_, state->dialog_input);
int scroll_x = 0;
if (total_w > max_w) scroll_x = total_w - max_w;
char sub_[128]; int k_=0;
for(k_=0; k_<state->dialog_input_cursor && state->dialog_input[k_]; k_++) sub_[k_]=state->dialog_input[k_];
sub_[k_]=0;
int cx_ = font_manager_get_string_width(ttf_, sub_) - scroll_x;
if (cx_ < 0) cx_ = 0;
if (cx_ > max_w) cx_ = max_w;
draw_rect(dlg_x+15+cx_, dlg_y+39, 2, 12, COLOR_WHITE); }
// Buttons (rounded)
draw_rounded_rect_filled(dlg_x + 50, dlg_y + 65, 80, 25, 6, COLOR_DARK_BORDER);
@@ -1203,7 +1249,19 @@ static void explorer_paint(Window *win) {
draw_string(dlg_x + 10, dlg_y + 10, "Rename", COLOR_WHITE);
draw_rounded_rect_filled(dlg_x + 10, dlg_y + 35, 280, 20, 4, COLOR_DARK_BG);
draw_string(dlg_x + 15, dlg_y + 40, state->dialog_input, COLOR_WHITE);
draw_rect(dlg_x + 15 + state->dialog_input_cursor * 8, dlg_y + 39, 2, 12, COLOR_WHITE);
// Dynamic cursor — find start offset so text doesn't overflow the input box
{ int max_w = 265; /* input box is 280px wide with 15px left pad */
ttf_font_t *ttf_ = graphics_get_current_ttf();
int total_w = font_manager_get_string_width(ttf_, state->dialog_input);
int scroll_x = 0;
if (total_w > max_w) scroll_x = total_w - max_w;
char sub_[128]; int k_=0;
for(k_=0; k_<state->dialog_input_cursor && state->dialog_input[k_]; k_++) sub_[k_]=state->dialog_input[k_];
sub_[k_]=0;
int cx_ = font_manager_get_string_width(ttf_, sub_) - scroll_x;
if (cx_ < 0) cx_ = 0;
if (cx_ > max_w) cx_ = max_w;
draw_rect(dlg_x+15+cx_, dlg_y+39, 2, 12, COLOR_WHITE); }
draw_rounded_rect_filled(dlg_x + 50, dlg_y + 65, 80, 25, 6, COLOR_DARK_BORDER);
draw_string(dlg_x + 68, dlg_y + 72, "Rename", COLOR_WHITE);
draw_rounded_rect_filled(dlg_x + 170, dlg_y + 65, 80, 25, 6, COLOR_DARK_BORDER);

View File

@@ -11,7 +11,7 @@
#define MAX_FILES 256
#define MAX_CLUSTERS 1024
#define MAX_CLUSTERS 8192
#define MAX_OPEN_HANDLES 32
// In-memory FAT table

159
src/kernel/font_manager.c Normal file
View File

@@ -0,0 +1,159 @@
#define STB_TRUETYPE_IMPLEMENTATION
#include "memory_manager.h"
#include "font_manager.h"
#include "stb_truetype.h"
#include "fat32.h"
#include <stddef.h>
// Simple math implementations for stb_truetype
float ksqrtf(float x) {
float res;
asm volatile("sqrtss %1, %0" : "=x"(res) : "x"(x));
return res;
}
float kfabsf(float x) {
return (x < 0) ? -x : x;
}
float kpowf(float b, float e) {
// Very simplified pow for stb_truetype's needs
if (e == 0) return 1.0f;
if (e == 1) return b;
if (e == 0.5f) return ksqrtf(b);
// Fallback/log-based would be complex, let's see if this suffices
float res = 1.0f;
for (int i = 0; i < (int)e; i++) res *= b;
return res;
}
float kfmodf(float x, float y) {
return x - (int)(x / y) * y;
}
float kcosf(float x) {
// Taylor series for cos(x) around 0
float x2 = x * x;
return 1.0f - (x2 / 2.0f) + (x2 * x2 / 24.0f) - (x2 * x2 * x2 / 720.0f);
}
float kacosf(float x) {
// Very rough approximation for acos(x)
if (x >= 1.0f) return 0;
if (x <= -1.0f) return 3.14159f;
return 1.57079f - x - (x*x*x)/6.0f;
}
extern void serial_write(const char *s);
extern uint32_t graphics_get_pixel(int x, int y);
static inline uint32_t alpha_blend(uint32_t bg, uint32_t fg, uint8_t alpha) {
if (alpha == 0) return bg;
if (alpha == 255) return fg;
uint32_t rb = (((fg & 0xFF00FF) * alpha) + ((bg & 0xFF00FF) * (255 - alpha))) >> 8;
uint32_t g = (((fg & 0x00FF00) * alpha) + ((bg & 0x00FF00) * (255 - alpha))) >> 8;
return (rb & 0xFF00FF) | (g & 0x00FF00);
}
static ttf_font_t *default_font = NULL;
bool font_manager_init(void) {
// We'll load a default font later if available
return true;
}
ttf_font_t* font_manager_load(const char *path, float size) {
FAT32_FileHandle *fh = fat32_open(path, "r");
if (!fh || !fh->valid) {
serial_write("[FONT] Failed to open font file: ");
serial_write(path);
serial_write("\n");
return NULL;
}
uint32_t fsize = fh->size;
unsigned char *buffer = kmalloc(fsize);
if (!buffer) {
fat32_close(fh);
return NULL;
}
int read = fat32_read(fh, buffer, fsize);
fat32_close(fh);
ttf_font_t *font = kmalloc(sizeof(ttf_font_t));
if (!font) {
kfree(buffer);
return NULL;
}
stbtt_fontinfo *info = kmalloc(sizeof(stbtt_fontinfo));
if (!info) {
kfree(buffer);
kfree(font);
return NULL;
}
if (!stbtt_InitFont(info, buffer, 0)) {
serial_write("[FONT] Failed to init font: ");
serial_write(path);
serial_write("\n");
kfree(info);
kfree(buffer);
kfree(font);
return NULL;
}
font->data = buffer;
font->size = fsize;
font->info = info;
font->scale = stbtt_ScaleForPixelHeight(info, size);
stbtt_GetFontVMetrics(info, &font->ascent, &font->descent, &font->line_gap);
if (!default_font) default_font = font;
return font;
}
void font_manager_render_char(ttf_font_t *font, int x, int y, char c, uint32_t color, void (*put_pixel_fn)(int, int, uint32_t)) {
if (!font) font = default_font;
if (!font) return;
stbtt_fontinfo *info = (stbtt_fontinfo *)font->info;
int w, h, xoff, yoff;
unsigned char *bitmap = stbtt_GetCodepointBitmap(info, 0, font->scale, c, &w, &h, &xoff, &yoff);
if (bitmap) {
for (int row = 0; row < h; row++) {
for (int col = 0; col < w; col++) {
unsigned char alpha = bitmap[row * w + col];
if (alpha > 0) {
int px = x + col + xoff;
int py = y + row + yoff;
uint32_t bg = graphics_get_pixel(px, py);
put_pixel_fn(px, py, alpha_blend(bg, color, alpha));
}
}
}
stbtt_FreeBitmap(bitmap, NULL);
}
}
int font_manager_get_string_width(ttf_font_t *font, const char *s) {
if (!font) font = default_font;
if (!font || !s) return 0;
stbtt_fontinfo *info = (stbtt_fontinfo *)font->info;
int width = 0;
while (*s) {
int advance, lsb;
stbtt_GetCodepointHMetrics(info, *s, &advance, &lsb);
// Round per-character to match draw_string's accumulation
width += (int)(advance * font->scale + 0.5f);
s++;
}
return width;
}

53
src/kernel/font_manager.h Normal file
View File

@@ -0,0 +1,53 @@
#ifndef FONT_MANAGER_H
#define FONT_MANAGER_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
// stb_truetype math stubs
extern float ksqrtf(float x);
extern float kpowf(float b, float e);
extern float kfmodf(float x, float y);
extern float kcosf(float x);
extern float kacosf(float x);
extern float kfabsf(float x);
#define STBTT_ifloor(x) ((int)(x))
#define STBTT_iceil(x) ((int)(x + 0.999999f))
#define STBTT_sqrt(x) ksqrtf(x)
#define STBTT_pow(x,y) kpowf(x,y)
#define STBTT_fmod(x,y) kfmodf(x,y)
#define STBTT_cos(x) kcosf(x)
#define STBTT_acos(x) kacosf(x)
#define STBTT_fabs(x) kfabsf(x)
#define STBTT_assert(x) ((void)0)
// Memory management
#define STBTT_malloc(x,u) kmalloc(x)
#define STBTT_free(x,u) kfree(x)
// String functions
#define STBTT_memcpy mem_memcpy
#define STBTT_memset mem_memset
// Data types
typedef uint64_t STBTT_ptrsize;
typedef struct {
void *data;
size_t size;
void *info; // stbtt_fontinfo
float scale;
int ascent;
int descent;
int line_gap;
} ttf_font_t;
bool font_manager_init(void);
ttf_font_t* font_manager_load(const char *path, float size);
void font_manager_render_char(ttf_font_t *font, int x, int y, char c, uint32_t color, void (*put_pixel_fn)(int, int, uint32_t));
int font_manager_get_string_width(ttf_font_t *font, const char *s);
#endif

Binary file not shown.

BIN
src/kernel/fonts/inter.ttf Normal file

Binary file not shown.

BIN
src/kernel/fonts/times.ttf Normal file

Binary file not shown.

View File

@@ -5,10 +5,13 @@
#include "graphics.h"
#include "font.h"
#include "io.h"
#include "font_manager.h"
static struct limine_framebuffer *g_fb = NULL;
static uint32_t g_bg_color = 0xFF696969;
extern void serial_write(const char *str);
#define PATTERN_SIZE 128
static uint32_t g_bg_pattern[PATTERN_SIZE * PATTERN_SIZE];
static bool g_use_pattern = false;
@@ -32,6 +35,8 @@ static uint32_t *g_render_target = NULL;
static int g_rt_width = 0;
static int g_rt_height = 0;
static ttf_font_t *g_current_ttf = NULL;
void graphics_init(struct limine_framebuffer *fb) {
g_fb = fb;
g_dirty.active = false;
@@ -41,6 +46,25 @@ void graphics_init(struct limine_framebuffer *fb) {
}
}
void graphics_init_fonts(void) {
font_manager_init();
g_current_ttf = font_manager_load("/Library/Fonts/firamono.ttf", 15.0f);
if (!g_current_ttf) {
serial_write("[FONT] Falling back to bitmap font\n");
}
}
void graphics_set_font(const char *path) {
ttf_font_t *new_font = font_manager_load(path, 15.0f);
if (new_font) {
// TODO: free old font data if needed
g_current_ttf = new_font;
serial_write("[FONT] Switched to: ");
serial_write(path);
serial_write("\n");
}
}
int get_screen_width(void) {
return g_fb ? g_fb->width : 0;
}
@@ -133,7 +157,6 @@ void graphics_set_render_target(uint32_t *buffer, int w, int h) {
void put_pixel(int x, int y, uint32_t color) {
if (g_render_target) {
if (x >= 0 && x < g_rt_width && y >= 0 && y < g_rt_height) {
// No clipping in custom render targets yet, just bounding box
g_render_target[y * g_rt_width + x] = color;
}
return;
@@ -149,11 +172,24 @@ void put_pixel(int x, int y, uint32_t color) {
}
}
// Draw to back buffer
uint32_t pixel_offset = y * g_fb->width + x;
g_back_buffer[pixel_offset] = color;
}
uint32_t graphics_get_pixel(int x, int y) {
if (g_render_target) {
if (x >= 0 && x < g_rt_width && y >= 0 && y < g_rt_height) {
return g_render_target[y * g_rt_width + x];
}
return 0;
}
if (!g_fb) return 0;
if (x < 0 || x >= (int)g_fb->width || y < 0 || y >= (int)g_fb->height) return 0;
return g_back_buffer[y * g_fb->width + x];
}
void draw_rect(int x, int y, int w, int h, uint32_t color) {
int x1 = x, y1 = y, x2 = x + w, y2 = y + h;
@@ -268,6 +304,11 @@ void draw_rounded_rect_filled(int x, int y, int w, int h, int radius, uint32_t c
}
void draw_char(int x, int y, char c, uint32_t color) {
if (g_current_ttf) {
font_manager_render_char(g_current_ttf, x, y, c, color, put_pixel);
return;
}
unsigned char uc = (unsigned char)c;
if (uc > 127) return;
@@ -289,9 +330,80 @@ void draw_char(int x, int y, char c, uint32_t color) {
}
}
// Bitmap-only version for terminal — always uses 8x8 bitmap font regardless of TTF
void draw_char_bitmap(int x, int y, char c, uint32_t color) {
unsigned char uc = (unsigned char)c;
if (uc > 127) return;
if (g_clip_enabled && !g_render_target) {
if (x + 8 <= g_clip_x || x >= g_clip_x + g_clip_w ||
y + 8 <= g_clip_y || y >= g_clip_y + g_clip_h) {
return;
}
}
const uint8_t *glyph = font8x8_basic[uc];
for (int row = 0; row < 8; row++) {
for (int col = 0; col < 8; col++) {
if ((glyph[row] >> (7 - col)) & 1) {
put_pixel(x + col, y + row, color);
}
}
}
}
ttf_font_t *graphics_get_current_ttf(void) {
return g_current_ttf;
}
void draw_string_bitmap(int x, int y, const char *str, uint32_t color) {
const char *s = str;
int cur_x = x;
int cur_y = y;
while (*s) {
if (*s == '\n') {
cur_x = x;
cur_y += 10;
} else {
draw_char_bitmap(cur_x, cur_y, *s, color);
cur_x += 8;
}
s++;
}
}
int graphics_get_font_height(void) {
if (g_current_ttf) {
return (int)((g_current_ttf->ascent - g_current_ttf->descent) * g_current_ttf->scale);
}
return 10; // Fallback bitmap height
}
void draw_string(int x, int y, const char *s, uint32_t color) {
if (!s) return;
int cur_x = x;
if (g_current_ttf) {
float scale = g_current_ttf->scale;
// Shift baseline up by roughly 2 pixels for better vertical centering in bars/inputs
int baseline = y + (int)(g_current_ttf->ascent * scale) - 2;
int line_height = (int)((g_current_ttf->ascent - g_current_ttf->descent + g_current_ttf->line_gap) * scale);
while (*s) {
if (*s == '\n') {
cur_x = x;
baseline += line_height;
} else {
font_manager_render_char(g_current_ttf, cur_x, baseline, *s, color, put_pixel);
// Advance by same rounded width that font_manager_get_string_width uses
char buf[2] = {*s, 0};
cur_x += font_manager_get_string_width(g_current_ttf, buf);
}
s++;
}
return;
}
int cur_y = y;
while (*s) {
if (*s == '\n') {

View File

@@ -15,11 +15,14 @@ typedef struct {
} DirtyRect;
void graphics_init(struct limine_framebuffer *fb);
void graphics_init_fonts(void);
void put_pixel(int x, int y, uint32_t color);
uint32_t graphics_get_pixel(int x, int y);
void draw_rect(int x, int y, int w, int h, uint32_t color);
void draw_rounded_rect(int x, int y, int w, int h, int radius, uint32_t color);
void draw_rounded_rect_filled(int x, int y, int w, int h, int radius, uint32_t color);
void draw_char(int x, int y, char c, uint32_t color);
void draw_char_bitmap(int x, int y, char c, uint32_t color);
void draw_string(int x, int y, const char *s, uint32_t color);
void draw_desktop_background(void);
void graphics_set_bg_color(uint32_t color);
@@ -49,4 +52,10 @@ void graphics_clear_back_buffer(uint32_t color);
void graphics_set_clipping(int x, int y, int w, int h);
void graphics_clear_clipping(void);
// Font access (requires font_manager.h for ttf_font_t)
#include "font_manager.h"
ttf_font_t *graphics_get_current_ttf(void);
int graphics_get_font_height(void);
void graphics_set_font(const char *path);
#endif

View File

@@ -11,6 +11,8 @@
#define GUI_CMD_GET_EVENT 5
#define GUI_CMD_DRAW_ROUNDED_RECT_FILLED 6
#define GUI_CMD_DRAW_IMAGE 7
#define GUI_CMD_GET_STRING_WIDTH 8
#define GUI_CMD_GET_FONT_HEIGHT 9
#define GUI_EVENT_NONE 0
#define GUI_EVENT_PAINT 1

View File

@@ -155,6 +155,7 @@ void kmain(void) {
fat32_mkdir("/Library");
fat32_mkdir("/Library/images");
fat32_mkdir("/Library/images/Wallpapers");
fat32_mkdir("/Library/Fonts");
if (module_request.response == NULL) {
serial_write("[DEBUG] ERROR: Limine Module Response is NULL!\n");
@@ -165,26 +166,27 @@ void kmain(void) {
for (uint64_t i = 0; i < module_request.response->module_count; i++) {
struct limine_file *mod = module_request.response->modules[i];
const char *clean_path = mod->path;
// Strip boot():/ or boot:/// prefixes common in different Limine versions
if (fs_starts_with(clean_path, "boot():")) clean_path += 7;
else if (fs_starts_with(clean_path, "boot:///")) clean_path += 8;
FAT32_FileHandle *fh = fat32_open(clean_path, "w");
if (fh && fh->valid) {
int written = fat32_write(fh, mod->address, mod->size);
fat32_write(fh, mod->address, mod->size);
fat32_close(fh);
} else {
serial_write("[DEBUG] ERROR: Failed to create file in RAMFS for module: ");
serial_write(clean_path);
serial_write("\n");
}
}
}
// Initialize fonts now that FAT32 and modules are loaded
uint64_t current_rsp;
asm volatile("mov %%rsp, %0" : "=r"(current_rsp));
serial_write("[DEBUG] Stack Alignment: 0x");
serial_write_hex(current_rsp);
serial_write("\n");
graphics_init_fonts();
asm("cli");
ps2_init();
asm("sti");

View File

@@ -23,6 +23,23 @@ void platform_init(void) {
kernel_phys_base = kernel_addr_request.response->physical_base;
kernel_virt_base = kernel_addr_request.response->virtual_base;
}
// Enable FPU and SSE
uint64_t cr0;
asm volatile("mov %%cr0, %0" : "=r"(cr0));
cr0 &= ~(1ULL << 2); // Clear EM (Emulation)
cr0 |= (1ULL << 1); // Set MP (Monitor Coprocessor)
cr0 |= (1ULL << 5); // Set NE (Numeric Error)
asm volatile("mov %0, %%cr0" : : "r"(cr0));
uint64_t cr4;
asm volatile("mov %%cr4, %0" : "=r"(cr4));
cr4 |= (1ULL << 9); // Set OSFXSR (FXSAVE/FXRSTOR support)
cr4 |= (1ULL << 10); // Set OSXMMEXCPT (SIMD exception support)
asm volatile("mov %0, %%cr4" : : "r"(cr4));
// Initialize FPU
asm volatile("fninit");
}
uint64_t p2v(uint64_t phys) { return phys + hhdm_offset; }
uint64_t v2p(uint64_t virt) {

View File

@@ -15,7 +15,7 @@ extern void cmd_write(const char *str);
extern void serial_write(const char *str);
#define MAX_PROCESSES 16
static process_t processes[MAX_PROCESSES];
static process_t processes[MAX_PROCESSES] __attribute__((aligned(16)));
static int process_count = 0;
static process_t* current_process = NULL;
static uint32_t next_pid = 0;
@@ -31,6 +31,10 @@ void process_init(void) {
kernel_proc->pml4_phys = paging_get_pml4_phys();
kernel_proc->kernel_stack = 0;
// Initialize FPU state for kernel
asm volatile("fxsave %0" : "=m"(kernel_proc->fpu_state));
kernel_proc->fpu_initialized = true;
for (int i = 0; i < MAX_PROCESS_FDS; i++) kernel_proc->fds[i] = NULL;
kernel_proc->next = kernel_proc; // Circular linked list
@@ -101,6 +105,11 @@ void process_create(void* entry_point, bool is_user) {
new_proc->kernel_stack = 0;
new_proc->rsp = (uint64_t)stack_ptr;
}
// Initialize FPU state for new process
asm volatile("fninit");
asm volatile("fxsave %0" : "=m"(new_proc->fpu_state));
new_proc->fpu_initialized = true;
// Add to linked list
new_proc->next = current_process->next;
@@ -260,6 +269,11 @@ process_t* process_create_elf(const char* filepath, const char* args_str) {
new_proc->user_stack_alloc = stack;
new_proc->rsp = (uint64_t)stack_ptr;
// Initialize FPU state for new process
asm volatile("fninit");
asm volatile("fxsave %0" : "=m"(new_proc->fpu_state));
new_proc->fpu_initialized = true;
// Slot is already counted in process_count if new, or reused.
// Add to linked list
@@ -285,8 +299,18 @@ uint64_t process_schedule(uint64_t current_rsp) {
// Save context
current_process->rsp = current_rsp;
// Save FPU state
if (current_process->fpu_initialized) {
asm volatile("fxsave %0" : "=m"(current_process->fpu_state));
}
// Switch process
current_process = current_process->next;
// Restore FPU state
if (current_process->fpu_initialized) {
asm volatile("fxrstor %0" : : "m"(current_process->fpu_state));
}
// Update Kernel Stack for User Mode interrupts and System Calls
if (current_process->is_user && current_process->kernel_stack) {

View File

@@ -41,10 +41,13 @@ typedef struct process {
void *kernel_stack_alloc; // Original pointer from kmalloc for freeing
void *user_stack_alloc; // Original pointer from kmalloc for freeing
bool is_terminal_proc; // Was this process started from the shell?
bool is_terminal_proc;
struct process *next;
} process_t;
uint8_t fpu_state[512] __attribute__((aligned(16)));
bool fpu_initialized;
} __attribute__((aligned(16))) process_t;
void process_init(void);
void process_create(void* entry_point, bool is_user);

5079
src/kernel/stb_truetype.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -16,6 +16,8 @@
#include "network.h"
#include "icmp.h"
#include "cmd.h"
#include "font_manager.h"
#include "graphics.h"
// Read MSR
static inline uint64_t rdmsr(uint32_t msr) {
@@ -348,6 +350,41 @@ static uint64_t syscall_handler_inner(uint64_t syscall_num, uint64_t arg1, uint6
draw_string(win->x + ux, win->y + uy, kernel_str, color);
}
asm volatile("push %0; popfq" : : "r"(rflags));
}
} else if (cmd == 10) { // GUI_CMD_DRAW_STRING_BITMAP
Window *win = (Window *)arg2;
uint64_t coords = arg3;
int ux = coords & 0xFFFFFFFF;
int uy = coords >> 32;
const char *user_str = (const char *)arg4;
uint32_t color = (uint32_t)arg5;
if (win && user_str) {
extern void draw_string_bitmap(int x, int y, const char *str, uint32_t color);
extern void graphics_set_render_target(uint32_t *buffer, int w, int h);
// Copy string safely to kernel stack buffer
char kernel_str[256];
int i = 0;
while (i < 255 && user_str[i]) {
kernel_str[i] = user_str[i];
i++;
}
kernel_str[i] = 0;
uint64_t rflags;
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
if (win->pixels) {
if (ux >= -100 && ux < win->w && uy >= -100 && uy < (win->h - 20)) {
graphics_set_render_target(win->pixels, win->w, win->h - 20);
draw_string_bitmap(ux, uy, kernel_str, color);
graphics_set_render_target(NULL, 0, 0);
}
} else {
draw_string_bitmap(win->x + ux, win->y + uy, kernel_str, color);
}
asm volatile("push %0; popfq" : : "r"(rflags));
}
} else if (cmd == GUI_CMD_DRAW_IMAGE) {
@@ -409,6 +446,27 @@ static uint64_t syscall_handler_inner(uint64_t syscall_num, uint64_t arg1, uint6
return 1;
}
return 0;
} else if (cmd == GUI_CMD_GET_STRING_WIDTH) {
const char *user_str = (const char *)arg2;
if (!user_str) return 0;
char kernel_str[256];
int i = 0;
while (i < 255 && user_str[i]) {
kernel_str[i] = user_str[i];
i++;
}
kernel_str[i] = 0;
ttf_font_t *font = graphics_get_current_ttf();
if (font) {
return (uint64_t)font_manager_get_string_width(font, kernel_str);
} else {
return (uint64_t)i * 8; // Fallback bitmap width
}
} else if (cmd == GUI_CMD_GET_FONT_HEIGHT) {
extern int graphics_get_font_height(void);
return (uint64_t)graphics_get_font_height();
}
} else if (syscall_num == SYS_FS) {
int cmd = (int)arg1;
@@ -548,14 +606,12 @@ static uint64_t syscall_handler_inner(uint64_t syscall_num, uint64_t arg1, uint6
} else if (cmd == 2) { // SYSTEM_CMD_SET_BG_PATTERN
uint32_t *user_pat = (uint32_t *)arg2;
if (!user_pat) {
extern void graphics_set_bg_pattern(uint32_t *pattern);
graphics_set_bg_pattern(NULL);
} else {
static uint32_t global_bg_pattern[128*128];
for (int i=0; i<128*128; i++) {
global_bg_pattern[i] = user_pat[i];
}
extern void graphics_set_bg_pattern(uint32_t *pattern);
graphics_set_bg_pattern(global_bg_pattern);
}
extern void wm_refresh(void);
@@ -778,6 +834,18 @@ static uint64_t syscall_handler_inner(uint64_t syscall_num, uint64_t arg1, uint6
extern void network_force_unlock(void);
network_force_unlock();
return 0;
} else if (cmd == 40) { // SYSTEM_CMD_SET_FONT
const char *user_path = (const char *)arg2;
if (!user_path) return -1;
// Copy font path from userland
char path[128];
int i;
for (i = 0; i < 127 && user_path[i]; i++) {
path[i] = user_path[i];
}
path[i] = 0;
graphics_set_font(path);
return 0;
}
return -1;
}

View File

@@ -40,8 +40,8 @@ static void draw_ansi_string(ui_window_t win, int x, int y, const char *str) {
if (*str == '\033' && *(str + 1) == '[') {
if (seg_idx > 0) {
segment[seg_idx] = 0;
ui_draw_string(win, current_x, y, segment, current_color);
current_x += seg_idx * 8;
ui_draw_string_bitmap(win, current_x, y, segment, current_color);
current_x += seg_idx * 8; // Bitmap font is exactly 8px wide
seg_idx = 0;
}
@@ -62,7 +62,7 @@ static void draw_ansi_string(ui_window_t win, int x, int y, const char *str) {
if (seg_idx > 0) {
segment[seg_idx] = 0;
ui_draw_string(win, current_x, y, segment, current_color);
ui_draw_string_bitmap(win, current_x, y, segment, current_color);
}
}
@@ -78,7 +78,7 @@ static void draw_ascii_logo(ui_window_t win, int x, int y) {
};
for (int i = 0; logo[i] != NULL; i++) {
draw_ansi_string(win, x, y + (i * 10), logo[i]);
draw_ansi_string(win, x, y + (i * 10), logo[i]); // Bitmap line height is 10px
}
}
@@ -86,20 +86,22 @@ static void about_paint(ui_window_t win) {
int w = 340;
int h = 240;
// Clear background to prevent alpha-blended text from accumulating on repaints
ui_draw_rect(win, 0, 0, w, h, 0xFF1E1E1E);
int offset_x = 15;
int offset_y = 35;
draw_ascii_logo(win, 14, offset_y);
// Version info
int fh = ui_get_font_height();
ui_draw_string(win, offset_x, offset_y + 105, "BoredOS 'Retrowave'", 0xFFFFFFFF);
ui_draw_string(win, offset_x, offset_y + 120, "BoredOS Version 1.65", 0xFFFFFFFF);
ui_draw_string(win, offset_x, offset_y + 135, "Kernel Version 3.0.1", 0xFFFFFFFF);
ui_draw_string(win, offset_x, offset_y + 105 + fh, "BoredOS Version 1.65", 0xFFFFFFFF);
ui_draw_string(win, offset_x, offset_y + 105 + fh*2, "Kernel Version 3.0.1", 0xFFFFFFFF);
// Copyright
ui_draw_string(win, offset_x, offset_y + 150, "(C) 2026 boreddevnl.", 0xFFFFFFFF);
ui_draw_string(win, offset_x, offset_y + 165, "All rights reserved.", 0xFFFFFFFF);
ui_draw_string(win, offset_x, offset_y + 105 + fh*3, "(C) 2026 boreddevnl.", 0xFFFFFFFF);
ui_draw_string(win, offset_x, offset_y + 105 + fh*4, "All rights reserved.", 0xFFFFFFFF);
ui_mark_dirty(win, 0, 0, w, h);
}

View File

@@ -452,7 +452,7 @@ static void parse_html(const char *html) {
while(ph[l] && ph[l] != '\"' && l < 255) { el->attr_value[l] = ph[l]; l++; }
el->attr_value[l] = 0;
} else el->attr_value[0] = 0;
if (el->tag == TAG_BUTTON) el->w = (int)strlen(el->attr_value) * 8 + 20;
if (el->tag == TAG_BUTTON) el->w = ui_get_string_width(el->attr_value) + 20;
line_elements[line_element_count++] = element_count - 1;
if (is_centered || cur_line_x + el->w > WIN_W - SCROLL_BAR_W - 20) flush_line(is_centered);
else cur_line_x += el->w;
@@ -471,14 +471,14 @@ static void parse_html(const char *html) {
word[w_idx] = 0;
if (w_idx > 0) {
if (element_count >= MAX_ELEMENTS) break;
int word_w = w_idx * 8;
int word_w = ui_get_string_width(word);
if (cur_line_x + word_w > WIN_W - SCROLL_BAR_W - 20) flush_line(is_centered);
RenderElement *el = &elements[element_count++];
for (int k=0; k<(int)sizeof(RenderElement); k++) ((char*)el)[k] = 0;
int k=0; while(word[k]) { el->content[k] = word[k]; k++; }
el->content[k++] = ' '; el->content[k] = 0;
el->w = (w_idx + 1) * 8; el->h = 16;
el->w = ui_get_string_width(el->content); el->h = 16;
el->tag = TAG_NONE; el->color = current_link[0] ? COLOR_LINK : current_color;
el->centered = is_centered; el->bold = is_bold;
if (current_link[0]) { int k=0; while(current_link[k]) { el->link_url[k] = current_link[k]; k++; } el->link_url[k] = 0; }
@@ -526,7 +526,12 @@ static void browser_paint(void) {
if (focused_element == i) {
int cursor_pos = el->input_cursor - el->input_scroll;
if (cursor_pos >= 0 && cursor_pos < max_v) {
ui_draw_rect(win_browser, el->x + 5 + cursor_pos * 8, draw_y + 16, 8, 2, 0xFF000000);
char sub[64];
int k;
for (k = 0; k < cursor_pos && visible[k]; k++) sub[k] = visible[k];
sub[k] = 0;
int cx = ui_get_string_width(sub);
ui_draw_rect(win_browser, el->x + 5 + cx, draw_y + 16, 8, 2, 0xFF000000);
}
}
} else if (el->tag == TAG_BUTTON) {
@@ -545,7 +550,12 @@ static void browser_paint(void) {
ui_draw_rect(win_browser, 0, 0, WIN_W, URL_BAR_H, COLOR_URL_BAR);
ui_draw_string(win_browser, 10, 8, url_input_buffer, COLOR_URL_TEXT);
if (focused_element == -1) {
ui_draw_rect(win_browser, 10 + url_cursor * 8, 22, 8, 2, COLOR_URL_TEXT);
char sub[512];
int k;
for (k = 0; k < url_cursor && url_input_buffer[k]; k++) sub[k] = url_input_buffer[k];
sub[k] = 0;
int cx = ui_get_string_width(sub);
ui_draw_rect(win_browser, 10 + cx, 22, 8, 2, COLOR_URL_TEXT);
}
// Scroll bar

View File

@@ -35,6 +35,11 @@ void ui_draw_string(ui_window_t win, int x, int y, const char *str, uint32_t col
syscall5(SYS_GUI, GUI_CMD_DRAW_STRING, (uint64_t)win, coords, (uint64_t)str, (uint64_t)color);
}
void ui_draw_string_bitmap(ui_window_t win, int x, int y, const char *str, uint32_t color) {
uint64_t coords = ((uint64_t)x & 0xFFFFFFFF) | ((uint64_t)y << 32);
syscall5(SYS_GUI, GUI_CMD_DRAW_STRING_BITMAP, (uint64_t)win, coords, (uint64_t)str, (uint64_t)color);
}
void ui_mark_dirty(ui_window_t win, int x, int y, int w, int h) {
uint64_t params[4] = { (uint64_t)x, (uint64_t)y, (uint64_t)w, (uint64_t)h };
syscall3(SYS_GUI, GUI_CMD_MARK_DIRTY, (uint64_t)win, (uint64_t)params);
@@ -44,3 +49,11 @@ void ui_draw_image(ui_window_t win, int x, int y, int w, int h, uint32_t *image_
uint64_t params[4] = { (uint64_t)x, (uint64_t)y, (uint64_t)w, (uint64_t)h };
syscall4(SYS_GUI, GUI_CMD_DRAW_IMAGE, (uint64_t)win, (uint64_t)params, (uint64_t)image_data);
}
uint32_t ui_get_string_width(const char *str) {
return (uint32_t)syscall3(SYS_GUI, GUI_CMD_GET_STRING_WIDTH, (uint64_t)str, 0);
}
uint32_t ui_get_font_height(void) {
return (uint32_t)syscall3(SYS_GUI, GUI_CMD_GET_FONT_HEIGHT, 0, 0);
}

View File

@@ -12,6 +12,9 @@
#define GUI_CMD_GET_EVENT 5
#define GUI_CMD_DRAW_ROUNDED_RECT_FILLED 6
#define GUI_CMD_DRAW_IMAGE 7
#define GUI_CMD_GET_STRING_WIDTH 8
#define GUI_CMD_GET_FONT_HEIGHT 9
#define GUI_CMD_DRAW_STRING_BITMAP 10
// Event Types
#define GUI_EVENT_NONE 0
@@ -45,5 +48,8 @@ void ui_draw_rounded_rect_filled(ui_window_t win, int x, int y, int w, int h, in
void ui_draw_string(ui_window_t win, int x, int y, const char *str, uint32_t color);
void ui_mark_dirty(ui_window_t win, int x, int y, int w, int h);
void ui_draw_image(ui_window_t win, int x, int y, int w, int h, uint32_t *image_data);
uint32_t ui_get_string_width(const char *str);
uint32_t ui_get_font_height(void);
void ui_draw_string_bitmap(ui_window_t win, int x, int y, const char *str, uint32_t color);
#endif

View File

@@ -16,7 +16,9 @@ static int cursor_pos = 0;
static int notepad_scroll_line = 0;
static void notepad_ensure_cursor_visible(int h) {
int visible_lines = (h - 10) / 10;
int fh = (int)ui_get_font_height();
if (fh < 8) fh = 8;
int visible_lines = (h - 10) / fh;
if (visible_lines < 1) visible_lines = 1;
int cursor_line = 0;
@@ -58,6 +60,9 @@ static void notepad_save_state() {
static void notepad_paint(ui_window_t win, int w, int h) {
ui_draw_rect(win, 0, 0, w, h, COLOR_NOTEPAD_BG);
int fh = (int)ui_get_font_height();
if (fh < 8) fh = 8;
int visual_line = 0;
int current_x = 4;
int current_y = 4;
@@ -70,38 +75,43 @@ static void notepad_paint(ui_window_t win, int w, int h) {
current_x = 4;
current_y = 4;
} else {
if (current_x >= window_right) {
char ch[2] = {buffer[i], 0};
int cw = (int)ui_get_string_width(ch);
if (cw < 1) cw = 8;
if (current_x + cw >= window_right) {
visual_line++;
current_x = 4;
current_y += 10;
current_y += fh;
}
current_x += 8;
current_x += cw;
}
continue;
}
if (visual_line >= notepad_scroll_line + (h - 8) / 10) {
if (visual_line >= notepad_scroll_line + (h - 8) / fh) {
break;
}
if (buffer[i] == '\n') {
current_x = 4;
current_y += 10;
current_y += fh;
visual_line++;
} else {
if (current_x >= window_right) {
char ch[2] = {buffer[i], 0};
int cw = (int)ui_get_string_width(ch);
if (cw < 1) cw = 8;
if (current_x + cw >= window_right) {
current_x = 4;
current_y += 10;
current_y += fh;
visual_line++;
if (visual_line >= notepad_scroll_line + (h - 8) / 10) {
if (visual_line >= notepad_scroll_line + (h - 8) / fh) {
break;
}
}
char ch[2] = {buffer[i], 0};
ui_draw_string(win, current_x, current_y, ch, COLOR_BLACK);
current_x += 8;
current_x += cw;
}
}
@@ -113,21 +123,24 @@ static void notepad_paint(ui_window_t win, int w, int h) {
for (int i = 0; i < cursor_pos; i++) {
if (buffer[i] == '\n') {
cx = 4;
cy += 10;
cy += fh;
c_visual_line++;
} else {
if (cx >= window_right) {
char ch[2] = {buffer[i], 0};
int cw = (int)ui_get_string_width(ch);
if (cw < 1) cw = 8;
if (cx + cw >= window_right) {
cx = 4;
cy += 10;
cy += fh;
c_visual_line++;
}
cx += 8;
cx += cw;
}
}
if (c_visual_line >= notepad_scroll_line &&
c_visual_line < notepad_scroll_line + (h - 8) / 10) {
ui_draw_rect(win, cx, cy, 2, 8, COLOR_BLACK);
c_visual_line < notepad_scroll_line + (h - 8) / fh) {
ui_draw_rect(win, cx, cy, 2, fh - 2, COLOR_BLACK);
}
ui_mark_dirty(win, 0, 0, w, h);

View File

@@ -28,6 +28,7 @@
#define VIEW_NETWORK 2
#define VIEW_DESKTOP 3
#define VIEW_MOUSE 4
#define VIEW_FONTS 5
static int current_view = VIEW_MAIN;
static char rgb_r[4] = "";
@@ -63,6 +64,16 @@ static int desktop_max_rows_per_col = 10;
static int desktop_max_cols = 10;
static int mouse_speed = 10;
// Font selection
#define MAX_FONTS 16
typedef struct {
char path[128];
char name[48];
} font_entry_t;
static font_entry_t fonts[MAX_FONTS];
static int font_count = 0;
static int selected_font = -1;
static void cli_itoa(int num, char *str) {
if (num == 0) {
str[0] = '0';
@@ -224,6 +235,14 @@ static void control_panel_paint_main(ui_window_t win) {
ui_draw_rect(win, offset_x + 20, offset_y + item_y + 10, 16, 10, 0xFFB0B0B0);
ui_draw_string(win, offset_x + 60, offset_y + item_y + 15, "Mouse", COLOR_DARK_TEXT);
ui_draw_string(win, offset_x + 60, offset_y + item_y + 35, "Pointer settings", COLOR_DKGRAY);
// Fonts
item_y += item_h + item_spacing;
ui_draw_rounded_rect_filled(win, offset_x, offset_y + item_y, win_w - 16, item_h, 8, COLOR_DARK_PANEL);
// Font icon: "Aa" stylized
ui_draw_string(win, offset_x + 14, offset_y + item_y + 10, "Aa", 0xFF6A9EF5);
ui_draw_string(win, offset_x + 60, offset_y + item_y + 15, "Fonts", COLOR_DARK_TEXT);
ui_draw_string(win, offset_x + 60, offset_y + item_y + 35, "Choose system font", COLOR_DKGRAY);
}
static void control_panel_paint_wallpaper(ui_window_t win) {
@@ -429,6 +448,60 @@ static void control_panel_paint_mouse(ui_window_t win) {
ui_draw_string(win, offset_x + 280, section_y + 4, speed_str, COLOR_DARK_TEXT);
}
static void load_fonts(void) {
font_count = 0;
FAT32_FileInfo info[MAX_FONTS];
int count = sys_list("/Library/Fonts", info, MAX_FONTS);
if (count < 0) return;
for (int i = 0; i < count && font_count < MAX_FONTS; i++) {
if (info[i].is_directory) continue;
// check if .ttf (case-insensitive)
int len = 0; while (info[i].name[len]) len++;
if (len < 4) continue;
char c1 = info[i].name[len-1]; if (c1 >= 'A' && c1 <= 'Z') c1 += 32;
char c2 = info[i].name[len-2]; if (c2 >= 'A' && c2 <= 'Z') c2 += 32;
char c3 = info[i].name[len-3]; if (c3 >= 'A' && c3 <= 'Z') c3 += 32;
char c4 = info[i].name[len-4]; if (c4 >= 'A' && c4 <= 'Z') c4 += 32;
if (c4 != '.' || c3 != 't' || c2 != 't' || c1 != 'f') continue;
font_entry_t *fe = &fonts[font_count];
// Build full path
char *pref = "/Library/Fonts/";
int pl = 0; while (pref[pl]) { fe->path[pl] = pref[pl]; pl++; }
int nl = 0; while (info[i].name[nl]) { fe->path[pl+nl] = info[i].name[nl]; nl++; }
fe->path[pl+nl] = 0;
// Store display name (strip .ttf)
for (int j = 0; j < nl - 4 && j < 47; j++) fe->name[j] = info[i].name[j];
fe->name[(nl-4 < 47) ? nl-4 : 47] = 0;
font_count++;
}
}
static void control_panel_paint_fonts(ui_window_t win) {
int offset_x = 8;
int offset_y = 6;
ui_draw_rounded_rect_filled(win, offset_x, offset_y + 5, 80, 25, 6, COLOR_DARK_PANEL);
ui_draw_string(win, offset_x + 10, offset_y + 13, "< Back", COLOR_DARK_TEXT);
ui_draw_string(win, offset_x, offset_y + 40, "System Font:", COLOR_DARK_TEXT);
int item_y = offset_y + 60;
for (int i = 0; i < font_count; i++) {
uint32_t bg_color = (i == selected_font) ? 0xFF3D5A80 : COLOR_DARK_PANEL;
ui_draw_rounded_rect_filled(win, offset_x, item_y, 330, 35, 6, bg_color);
// Font icon
ui_draw_string(win, offset_x + 10, item_y + 9, "Aa", 0xFF6A9EF5);
// Font name
ui_draw_string(win, offset_x + 40, item_y + 9, fonts[i].name, COLOR_DARK_TEXT);
if (i == selected_font) {
ui_draw_string(win, offset_x + 290, item_y + 9, "*", 0xFF90EE90);
}
item_y += 40;
}
}
static void control_panel_paint(ui_window_t win) {
// Fill background
ui_draw_rect(win, 0, 0, 350, 500, COLOR_DARK_BG);
@@ -443,6 +516,8 @@ static void control_panel_paint(ui_window_t win) {
control_panel_paint_desktop(win);
} else if (current_view == VIEW_MOUSE) {
control_panel_paint_mouse(win);
} else if (current_view == VIEW_FONTS) {
control_panel_paint_fonts(win);
}
}
@@ -494,6 +569,11 @@ static void control_panel_handle_click(int x, int y) {
if (x >= offset_x && x < win_w - 8 && y >= item_y && y < item_y + item_h) {
current_view = VIEW_MOUSE;
}
item_y += item_h + item_spacing;
if (x >= offset_x && x < win_w - 8 && y >= item_y && y < item_y + item_h) {
current_view = VIEW_FONTS;
if (font_count == 0) load_fonts();
}
} else if (current_view == VIEW_WALLPAPER) {
int offset_x = 8;
int offset_y = 6;
@@ -657,6 +737,26 @@ static void control_panel_handle_click(int x, int y) {
save_mouse_config();
return;
}
} else if (current_view == VIEW_FONTS) {
int offset_x = 8;
int offset_y = 6;
// Back button
if (x >= offset_x && x < offset_x + 80 && y >= offset_y + 5 && y < offset_y + 30) {
current_view = VIEW_MAIN;
return;
}
// Font items
int item_y = offset_y + 60;
for (int i = 0; i < font_count; i++) {
if (x >= offset_x && x < offset_x + 330 && y >= item_y && y < item_y + 35) {
selected_font = i;
sys_system(40 /*SET_FONT*/, (uint64_t)fonts[i].path, 0, 0, 0);
return;
}
item_y += 40;
}
}
}

View File

@@ -1046,7 +1046,7 @@ void draw_window(Window *win) {
// Title Bar (rounded at top only - overdraw bottom to hide rounding)
draw_rounded_rect_filled(win->x, win->y, win->w, 20, 8, COLOR_DARK_TITLEBAR);
draw_rect(win->x, win->y + 12, win->w, 8, COLOR_DARK_TITLEBAR); // Cover bottom rounded corners
draw_string(win->x + 28, win->y + 5, win->title, COLOR_DARK_TEXT);
draw_string(win->x + 28, win->y + 4, win->title, COLOR_DARK_TEXT);
// Traffic Light (close button - red)
draw_traffic_light(win->x + 8, win->y + 2);
@@ -1061,6 +1061,23 @@ void draw_window(Window *win) {
graphics_blit_buffer(win->pixels, win->x, win->y + 20, win->w, win->h - 20);
}
// Mask bottom corners: clear pixels outside the rounded boundary
{
int radius = 8;
int bx = win->x;
int by = win->y + win->h - radius;
for (int dy = 0; dy < radius; dy++) {
int dx = isqrt(radius*radius - dy*dy);
int fill_w = radius - dx;
if (fill_w > 0) {
// Bottom-left corner
draw_rect(bx, by + dy, fill_w, 1, 0xFF000000);
// Bottom-right corner
draw_rect(bx + win->w - fill_w, by + dy, fill_w, 1, 0xFF000000);
}
}
}
if (win->paint) {
win->paint(win);
}
@@ -1332,7 +1349,12 @@ void wm_paint(void) {
draw_rounded_rect_filled(dlg_x + 10, dlg_y + 35, 280, 20, 4, COLOR_DARK_BG);
draw_string(dlg_x + 15, dlg_y + 40, desktop_dialog_input, COLOR_WHITE);
// Cursor
draw_rect(dlg_x + 15 + desktop_dialog_cursor * 8, dlg_y + 39, 2, 12, COLOR_WHITE);
char sub[64];
int k;
for (k = 0; k < desktop_dialog_cursor && desktop_dialog_input[k]; k++) sub[k] = desktop_dialog_input[k];
sub[k] = 0;
int cx = font_manager_get_string_width(graphics_get_current_ttf(), sub);
draw_rect(dlg_x + 15 + cx, dlg_y + 39, 2, 12, COLOR_WHITE);
draw_rounded_rect_filled(dlg_x + 50, dlg_y + 65, 80, 25, 4, COLOR_DARK_BORDER);
draw_string(dlg_x + 70, dlg_y + 72, btn_text, COLOR_WHITE);