.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

@ -31,7 +31,7 @@ OBJ_FILES = $(patsubst $(SRC_DIR)/%.c, $(BUILD_DIR)/%.o, $(C_SOURCES)) \
CFLAGS = -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding \ CFLAGS = -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding \
-fno-stack-protector -fno-stack-check -fno-lto -fPIE \ -fno-stack-protector -fno-stack-check -fno-lto -fPIE \
-m64 -march=x86-64 -mno-80387 -mno-mmx -mno-sse -mno-sse2 -mno-red-zone \ -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone \
-I$(SRC_DIR) -I$(SRC_DIR)/lwip -I$(SRC_DIR) -I$(SRC_DIR)/lwip
LDFLAGS = -m elf_x86_64 -nostdlib -static -pie --no-dynamic-linker \ LDFLAGS = -m elf_x86_64 -nostdlib -static -pie --no-dynamic-linker \
@ -133,6 +133,16 @@ $(ISO_IMAGE): $(KERNEL_ELF) limine.conf limine-setup
# Create EFI Boot Files # Create EFI Boot Files
cp limine/BOOTX64.EFI $(ISO_DIR)/EFI/BOOT/ cp limine/BOOTX64.EFI $(ISO_DIR)/EFI/BOOT/
cp limine/BOOTIA32.EFI $(ISO_DIR)/EFI/BOOT/ cp limine/BOOTIA32.EFI $(ISO_DIR)/EFI/BOOT/
# Copy Fonts
mkdir -p $(ISO_DIR)/Library/Fonts
@for f in $(SRC_DIR)/fonts/*.ttf; do \
if [ -f "$$f" ]; then \
basename=$$(basename "$$f"); \
cp "$$f" $(ISO_DIR)/Library/Fonts/; \
echo " module_path: boot():/Library/Fonts/$$basename" >> $(ISO_DIR)/limine.conf; \
fi \
done
# Generate ISO # Generate ISO
$(XORRISO) -as mkisofs -b limine-bios-cd.bin \ $(XORRISO) -as mkisofs -b limine-bios-cd.bin \
@ -152,6 +162,6 @@ run: $(ISO_IMAGE)
qemu-system-x86_64 -m 2G -serial stdio -cdrom $< -boot d \ qemu-system-x86_64 -m 2G -serial stdio -cdrom $< -boot d \
-m 4G \ -m 4G \
-audiodev coreaudio,id=audio0 -machine pcspk-audiodev=audio0 \ -audiodev coreaudio,id=audio0 -machine pcspk-audiodev=audio0 \
-netdev user,id=net0,hostfwd=udp::12345-:12345 -device e1000,netdev=net0 \ -netdev user,id=net0,hostfwd=udp::12346-:12345 -device e1000,netdev=net0 \
-vga std -global VGA.xres=1920 -global VGA.yres=1080 \ -vga std -global VGA.xres=1920 -global VGA.yres=1080 \
-drive file=disk.img,format=raw,file.locking=off -drive file=disk.img,format=raw,file.locking=off

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -25,8 +25,9 @@
#define DEFAULT_CMD_COLS 116 #define DEFAULT_CMD_COLS 116
#define DEFAULT_CMD_ROWS 41 #define DEFAULT_CMD_ROWS 41
#define MAX_CMD_COLS 256 #define MAX_CMD_COLS 256
// Terminal cell dimensions — fixed to match the 8x8 bitmap font
#define LINE_HEIGHT 10 #define LINE_HEIGHT 10
#define CHAR_WIDTH 8 #define CHAR_WIDTH 8
#define COLOR_RED 0xFFFF0000 #define COLOR_RED 0xFFFF0000
@ -1801,7 +1802,7 @@ static void cmd_paint(Window *win) {
if (r == cursor_row && c == cursor_col && win->focused) { if (r == cursor_row && c == cursor_col && win->focused) {
color = shell_config.bg_color; 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. // This header needs to maintain in any file it is present in, as per the GPL license terms.
#include "explorer.h" #include "explorer.h"
#include "graphics.h" #include "graphics.h"
#include "font_manager.h"
#include "fat32.h" #include "fat32.h"
#include "disk.h" #include "disk.h"
#include "wm.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 // Center in EXPLORER_ITEM_WIDTH
int l1_len = 0; while(line1[l1_len]) l1_len++; ttf_font_t *font = graphics_get_current_ttf();
int l1_w = l1_len * 8;
int l1_w = font_manager_get_string_width(font, line1);
draw_string(x + (EXPLORER_ITEM_WIDTH - l1_w)/2, y + 56, line1, color); draw_string(x + (EXPLORER_ITEM_WIDTH - l1_w)/2, y + 56, line1, color);
if (line2[0]) { if (line2[0]) {
int l2_len = 0; while(line2[l2_len]) l2_len++; int l2_w = font_manager_get_string_width(font, line2);
int l2_w = l2_len * 8;
draw_string(x + (EXPLORER_ITEM_WIDTH - l2_w)/2, y + 66, line2, color); 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_rect(offset_x, offset_y, win->w - 8, win->h - 28, COLOR_DARK_BG);
// Draw Drive Button (modern rounded style) // 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 // Extract drive from the window's current_path instead of using global current_drive
char current_drv = 'A'; char current_drv = 'A';
if (state->current_path[0] && state->current_path[1] == ':') { 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]; current_drv = state->current_path[0];
} }
drive_label[0] = '['; // Look up drive type
drive_label[1] = ' '; const char *type_str = "RAM";
drive_label[2] = current_drv; Disk *drv = disk_get_by_letter(current_drv);
drive_label[3] = ':'; if (drv) {
drive_label[4] = ' '; switch (drv->type) {
drive_label[5] = 'v'; case DISK_TYPE_RAM: type_str = "RAM"; break;
drive_label[6] = ' '; case DISK_TYPE_IDE: type_str = "IDE"; break;
drive_label[7] = ']'; case DISK_TYPE_SATA: type_str = "SATA"; break;
drive_label[8] = 0; case DISK_TYPE_USB: type_str = "USB"; break;
default: type_str = "???"; break;
}
}
// Button at x+4, y+3, w=60 (rounded) // Build label: "[ A:TYPE v ]"
draw_rounded_rect_filled(win->x + 4, offset_y + 3, 60, 22, 5, COLOR_DARK_PANEL); 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_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_height = 22;
int path_x = offset_x + 64; int path_x = offset_x + drive_label_w + 8;
int path_w = win->w - 16 - 64; 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_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 + 6, offset_y + 8, "Path:", COLOR_DARK_TEXT);
draw_string(path_x + 46, offset_y + 8, state->current_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) // Draw dropdown menu button (right-aligned, before back button, rounded)
int dropdown_btn_x = win->x + win->w - 90; int dropdown_btn_x = win->x + win->w - 90;
@ -1073,7 +1095,19 @@ static void explorer_paint(Window *win) {
// Input field (rounded dark) // Input field (rounded dark)
draw_rounded_rect_filled(dlg_x + 10, dlg_y + 35, 280, 20, 4, COLOR_DARK_BG); 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_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) // Buttons (rounded)
draw_rounded_rect_filled(dlg_x + 50, dlg_y + 65, 80, 25, 6, COLOR_DARK_BORDER); 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) // Input field (rounded dark)
draw_rounded_rect_filled(dlg_x + 10, dlg_y + 35, 280, 20, 4, COLOR_DARK_BG); 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_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) // Buttons (rounded)
draw_rounded_rect_filled(dlg_x + 50, dlg_y + 65, 80, 25, 6, COLOR_DARK_BORDER); 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_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_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_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_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_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); 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_FILES 256
#define MAX_CLUSTERS 1024 #define MAX_CLUSTERS 8192
#define MAX_OPEN_HANDLES 32 #define MAX_OPEN_HANDLES 32
// In-memory FAT table // 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 "graphics.h"
#include "font.h" #include "font.h"
#include "io.h" #include "io.h"
#include "font_manager.h"
static struct limine_framebuffer *g_fb = NULL; static struct limine_framebuffer *g_fb = NULL;
static uint32_t g_bg_color = 0xFF696969; static uint32_t g_bg_color = 0xFF696969;
extern void serial_write(const char *str);
#define PATTERN_SIZE 128 #define PATTERN_SIZE 128
static uint32_t g_bg_pattern[PATTERN_SIZE * PATTERN_SIZE]; static uint32_t g_bg_pattern[PATTERN_SIZE * PATTERN_SIZE];
static bool g_use_pattern = false; 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_width = 0;
static int g_rt_height = 0; static int g_rt_height = 0;
static ttf_font_t *g_current_ttf = NULL;
void graphics_init(struct limine_framebuffer *fb) { void graphics_init(struct limine_framebuffer *fb) {
g_fb = fb; g_fb = fb;
g_dirty.active = false; 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) { int get_screen_width(void) {
return g_fb ? g_fb->width : 0; 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) { void put_pixel(int x, int y, uint32_t color) {
if (g_render_target) { if (g_render_target) {
if (x >= 0 && x < g_rt_width && y >= 0 && y < g_rt_height) { 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; g_render_target[y * g_rt_width + x] = color;
} }
return; 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; uint32_t pixel_offset = y * g_fb->width + x;
g_back_buffer[pixel_offset] = color; 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) { 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; 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) { 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; unsigned char uc = (unsigned char)c;
if (uc > 127) return; 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) { void draw_string(int x, int y, const char *s, uint32_t color) {
if (!s) return; if (!s) return;
int cur_x = x; 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; int cur_y = y;
while (*s) { while (*s) {
if (*s == '\n') { if (*s == '\n') {

View file

@ -15,11 +15,14 @@ typedef struct {
} DirtyRect; } DirtyRect;
void graphics_init(struct limine_framebuffer *fb); void graphics_init(struct limine_framebuffer *fb);
void graphics_init_fonts(void);
void put_pixel(int x, int y, uint32_t color); 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_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(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_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(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_string(int x, int y, const char *s, uint32_t color);
void draw_desktop_background(void); void draw_desktop_background(void);
void graphics_set_bg_color(uint32_t color); 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_set_clipping(int x, int y, int w, int h);
void graphics_clear_clipping(void); 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 #endif

View file

@ -11,6 +11,8 @@
#define GUI_CMD_GET_EVENT 5 #define GUI_CMD_GET_EVENT 5
#define GUI_CMD_DRAW_ROUNDED_RECT_FILLED 6 #define GUI_CMD_DRAW_ROUNDED_RECT_FILLED 6
#define GUI_CMD_DRAW_IMAGE 7 #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_NONE 0
#define GUI_EVENT_PAINT 1 #define GUI_EVENT_PAINT 1

View file

@ -155,6 +155,7 @@ void kmain(void) {
fat32_mkdir("/Library"); fat32_mkdir("/Library");
fat32_mkdir("/Library/images"); fat32_mkdir("/Library/images");
fat32_mkdir("/Library/images/Wallpapers"); fat32_mkdir("/Library/images/Wallpapers");
fat32_mkdir("/Library/Fonts");
if (module_request.response == NULL) { if (module_request.response == NULL) {
serial_write("[DEBUG] ERROR: Limine Module Response is NULL!\n"); 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++) { for (uint64_t i = 0; i < module_request.response->module_count; i++) {
struct limine_file *mod = module_request.response->modules[i]; struct limine_file *mod = module_request.response->modules[i];
const char *clean_path = mod->path; 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; if (fs_starts_with(clean_path, "boot():")) clean_path += 7;
else if (fs_starts_with(clean_path, "boot:///")) clean_path += 8; else if (fs_starts_with(clean_path, "boot:///")) clean_path += 8;
FAT32_FileHandle *fh = fat32_open(clean_path, "w"); FAT32_FileHandle *fh = fat32_open(clean_path, "w");
if (fh && fh->valid) { if (fh && fh->valid) {
int written = fat32_write(fh, mod->address, mod->size); fat32_write(fh, mod->address, mod->size);
fat32_close(fh); 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"); asm("cli");
ps2_init(); ps2_init();
asm("sti"); asm("sti");

View file

@ -23,6 +23,23 @@ void platform_init(void) {
kernel_phys_base = kernel_addr_request.response->physical_base; kernel_phys_base = kernel_addr_request.response->physical_base;
kernel_virt_base = kernel_addr_request.response->virtual_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 p2v(uint64_t phys) { return phys + hhdm_offset; }
uint64_t v2p(uint64_t virt) { 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); extern void serial_write(const char *str);
#define MAX_PROCESSES 16 #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 int process_count = 0;
static process_t* current_process = NULL; static process_t* current_process = NULL;
static uint32_t next_pid = 0; static uint32_t next_pid = 0;
@ -31,6 +31,10 @@ void process_init(void) {
kernel_proc->pml4_phys = paging_get_pml4_phys(); kernel_proc->pml4_phys = paging_get_pml4_phys();
kernel_proc->kernel_stack = 0; 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; for (int i = 0; i < MAX_PROCESS_FDS; i++) kernel_proc->fds[i] = NULL;
kernel_proc->next = kernel_proc; // Circular linked list 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->kernel_stack = 0;
new_proc->rsp = (uint64_t)stack_ptr; 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 // Add to linked list
new_proc->next = current_process->next; 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->user_stack_alloc = stack;
new_proc->rsp = (uint64_t)stack_ptr; 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. // Slot is already counted in process_count if new, or reused.
// Add to linked list // Add to linked list
@ -285,8 +299,18 @@ uint64_t process_schedule(uint64_t current_rsp) {
// Save context // Save context
current_process->rsp = current_rsp; current_process->rsp = current_rsp;
// Save FPU state
if (current_process->fpu_initialized) {
asm volatile("fxsave %0" : "=m"(current_process->fpu_state));
}
// Switch process // Switch process
current_process = current_process->next; 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 // Update Kernel Stack for User Mode interrupts and System Calls
if (current_process->is_user && current_process->kernel_stack) { 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 *kernel_stack_alloc; // Original pointer from kmalloc for freeing
void *user_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; 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_init(void);
void process_create(void* entry_point, bool is_user); 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 "network.h"
#include "icmp.h" #include "icmp.h"
#include "cmd.h" #include "cmd.h"
#include "font_manager.h"
#include "graphics.h"
// Read MSR // Read MSR
static inline uint64_t rdmsr(uint32_t 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); 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)); asm volatile("push %0; popfq" : : "r"(rflags));
} }
} else if (cmd == GUI_CMD_DRAW_IMAGE) { } 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 1;
} }
return 0; 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) { } else if (syscall_num == SYS_FS) {
int cmd = (int)arg1; 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 } else if (cmd == 2) { // SYSTEM_CMD_SET_BG_PATTERN
uint32_t *user_pat = (uint32_t *)arg2; uint32_t *user_pat = (uint32_t *)arg2;
if (!user_pat) { if (!user_pat) {
extern void graphics_set_bg_pattern(uint32_t *pattern);
graphics_set_bg_pattern(NULL); graphics_set_bg_pattern(NULL);
} else { } else {
static uint32_t global_bg_pattern[128*128]; static uint32_t global_bg_pattern[128*128];
for (int i=0; i<128*128; i++) { for (int i=0; i<128*128; i++) {
global_bg_pattern[i] = user_pat[i]; global_bg_pattern[i] = user_pat[i];
} }
extern void graphics_set_bg_pattern(uint32_t *pattern);
graphics_set_bg_pattern(global_bg_pattern); graphics_set_bg_pattern(global_bg_pattern);
} }
extern void wm_refresh(void); 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); extern void network_force_unlock(void);
network_force_unlock(); network_force_unlock();
return 0; 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; 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 (*str == '\033' && *(str + 1) == '[') {
if (seg_idx > 0) { if (seg_idx > 0) {
segment[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);
current_x += seg_idx * 8; current_x += seg_idx * 8; // Bitmap font is exactly 8px wide
seg_idx = 0; 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) { if (seg_idx > 0) {
segment[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++) { 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 w = 340;
int h = 240; 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_x = 15;
int offset_y = 35; int offset_y = 35;
draw_ascii_logo(win, 14, offset_y); 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 + 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 + 105 + fh, "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*2, "Kernel Version 3.0.1", 0xFFFFFFFF);
// Copyright // Copyright
ui_draw_string(win, offset_x, offset_y + 150, "(C) 2026 boreddevnl.", 0xFFFFFFFF); ui_draw_string(win, offset_x, offset_y + 105 + fh*3, "(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*4, "All rights reserved.", 0xFFFFFFFF);
ui_mark_dirty(win, 0, 0, w, h); 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++; } while(ph[l] && ph[l] != '\"' && l < 255) { el->attr_value[l] = ph[l]; l++; }
el->attr_value[l] = 0; el->attr_value[l] = 0;
} else el->attr_value[0] = 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; 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); if (is_centered || cur_line_x + el->w > WIN_W - SCROLL_BAR_W - 20) flush_line(is_centered);
else cur_line_x += el->w; else cur_line_x += el->w;
@ -471,14 +471,14 @@ static void parse_html(const char *html) {
word[w_idx] = 0; word[w_idx] = 0;
if (w_idx > 0) { if (w_idx > 0) {
if (element_count >= MAX_ELEMENTS) break; 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); if (cur_line_x + word_w > WIN_W - SCROLL_BAR_W - 20) flush_line(is_centered);
RenderElement *el = &elements[element_count++]; RenderElement *el = &elements[element_count++];
for (int k=0; k<(int)sizeof(RenderElement); k++) ((char*)el)[k] = 0; 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++; } int k=0; while(word[k]) { el->content[k] = word[k]; k++; }
el->content[k++] = ' '; el->content[k] = 0; 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->tag = TAG_NONE; el->color = current_link[0] ? COLOR_LINK : current_color;
el->centered = is_centered; el->bold = is_bold; 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; } 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) { if (focused_element == i) {
int cursor_pos = el->input_cursor - el->input_scroll; int cursor_pos = el->input_cursor - el->input_scroll;
if (cursor_pos >= 0 && cursor_pos < max_v) { 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) { } 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_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); ui_draw_string(win_browser, 10, 8, url_input_buffer, COLOR_URL_TEXT);
if (focused_element == -1) { 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 // 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); 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) { 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 }; 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); 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 }; 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); 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_GET_EVENT 5
#define GUI_CMD_DRAW_ROUNDED_RECT_FILLED 6 #define GUI_CMD_DRAW_ROUNDED_RECT_FILLED 6
#define GUI_CMD_DRAW_IMAGE 7 #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 // Event Types
#define GUI_EVENT_NONE 0 #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_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_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); 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 #endif

View file

@ -16,7 +16,9 @@ static int cursor_pos = 0;
static int notepad_scroll_line = 0; static int notepad_scroll_line = 0;
static void notepad_ensure_cursor_visible(int h) { 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; if (visible_lines < 1) visible_lines = 1;
int cursor_line = 0; 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) { static void notepad_paint(ui_window_t win, int w, int h) {
ui_draw_rect(win, 0, 0, w, h, COLOR_NOTEPAD_BG); 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 visual_line = 0;
int current_x = 4; int current_x = 4;
int current_y = 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_x = 4;
current_y = 4; current_y = 4;
} else { } 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++; visual_line++;
current_x = 4; current_x = 4;
current_y += 10; current_y += fh;
} }
current_x += 8; current_x += cw;
} }
continue; continue;
} }
if (visual_line >= notepad_scroll_line + (h - 8) / 10) { if (visual_line >= notepad_scroll_line + (h - 8) / fh) {
break; break;
} }
if (buffer[i] == '\n') { if (buffer[i] == '\n') {
current_x = 4; current_x = 4;
current_y += 10; current_y += fh;
visual_line++; visual_line++;
} else { } 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_x = 4;
current_y += 10; current_y += fh;
visual_line++; visual_line++;
if (visual_line >= notepad_scroll_line + (h - 8) / 10) { if (visual_line >= notepad_scroll_line + (h - 8) / fh) {
break; break;
} }
} }
char ch[2] = {buffer[i], 0};
ui_draw_string(win, current_x, current_y, ch, COLOR_BLACK); 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++) { for (int i = 0; i < cursor_pos; i++) {
if (buffer[i] == '\n') { if (buffer[i] == '\n') {
cx = 4; cx = 4;
cy += 10; cy += fh;
c_visual_line++; c_visual_line++;
} else { } 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; cx = 4;
cy += 10; cy += fh;
c_visual_line++; c_visual_line++;
} }
cx += 8; cx += cw;
} }
} }
if (c_visual_line >= notepad_scroll_line && if (c_visual_line >= notepad_scroll_line &&
c_visual_line < notepad_scroll_line + (h - 8) / 10) { c_visual_line < notepad_scroll_line + (h - 8) / fh) {
ui_draw_rect(win, cx, cy, 2, 8, COLOR_BLACK); ui_draw_rect(win, cx, cy, 2, fh - 2, COLOR_BLACK);
} }
ui_mark_dirty(win, 0, 0, w, h); ui_mark_dirty(win, 0, 0, w, h);

View file

@ -28,6 +28,7 @@
#define VIEW_NETWORK 2 #define VIEW_NETWORK 2
#define VIEW_DESKTOP 3 #define VIEW_DESKTOP 3
#define VIEW_MOUSE 4 #define VIEW_MOUSE 4
#define VIEW_FONTS 5
static int current_view = VIEW_MAIN; static int current_view = VIEW_MAIN;
static char rgb_r[4] = ""; 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 desktop_max_cols = 10;
static int mouse_speed = 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) { static void cli_itoa(int num, char *str) {
if (num == 0) { if (num == 0) {
str[0] = '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_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 + 15, "Mouse", COLOR_DARK_TEXT);
ui_draw_string(win, offset_x + 60, offset_y + item_y + 35, "Pointer settings", COLOR_DKGRAY); 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) { 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); 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) { static void control_panel_paint(ui_window_t win) {
// Fill background // Fill background
ui_draw_rect(win, 0, 0, 350, 500, COLOR_DARK_BG); 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); control_panel_paint_desktop(win);
} else if (current_view == VIEW_MOUSE) { } else if (current_view == VIEW_MOUSE) {
control_panel_paint_mouse(win); 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) { if (x >= offset_x && x < win_w - 8 && y >= item_y && y < item_y + item_h) {
current_view = VIEW_MOUSE; 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) { } else if (current_view == VIEW_WALLPAPER) {
int offset_x = 8; int offset_x = 8;
int offset_y = 6; int offset_y = 6;
@ -657,6 +737,26 @@ static void control_panel_handle_click(int x, int y) {
save_mouse_config(); save_mouse_config();
return; 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) // 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_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_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) // Traffic Light (close button - red)
draw_traffic_light(win->x + 8, win->y + 2); 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); 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) { if (win->paint) {
win->paint(win); 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_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); draw_string(dlg_x + 15, dlg_y + 40, desktop_dialog_input, COLOR_WHITE);
// Cursor // 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_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); draw_string(dlg_x + 70, dlg_y + 72, btn_text, COLOR_WHITE);