src/kernel --> src/

This commit is contained in:
boreddevnl
2026-03-16 00:30:47 +01:00
parent 3da1496e4f
commit fc83d7941b
630 changed files with 2 additions and 2 deletions

1870
src/wm/explorer.c Normal file

File diff suppressed because it is too large Load Diff

88
src/wm/explorer.h Normal file
View File

@@ -0,0 +1,88 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#ifndef EXPLORER_H
#define EXPLORER_H
#include "wm.h"
#include "fat32.h"
#include <stddef.h>
// External windows references (for opening other apps)
extern Window win_explorer;
extern Window win_editor;
extern Window win_cmd;
extern Window win_notepad;
extern Window win_markdown;
#define EXPLORER_MAX_FILES 64
#define DIALOG_INPUT_MAX 256
typedef struct {
char name[FAT32_MAX_FILENAME];
bool is_directory;
uint32_t size;
uint32_t color;
} ExplorerItem;
typedef struct {
ExplorerItem items[EXPLORER_MAX_FILES];
int item_count;
int selected_item;
char current_path[FAT32_MAX_PATH];
int last_clicked_item;
uint32_t last_click_time;
int explorer_scroll_row;
// Dialog state
int dialog_state;
char dialog_input[DIALOG_INPUT_MAX];
int dialog_input_cursor;
char dialog_target_path[FAT32_MAX_PATH];
bool dialog_target_is_dir;
char dialog_dest_dir[FAT32_MAX_PATH];
char dialog_creation_path[FAT32_MAX_PATH];
char dialog_move_src[FAT32_MAX_PATH];
// Dropdown menu state
bool dropdown_menu_visible;
bool drive_menu_visible;
// File context menu state
bool file_context_menu_visible;
int file_context_menu_x;
int file_context_menu_y;
int file_context_menu_item;
} ExplorerState;
void explorer_init(void);
void explorer_reset(void);
void explorer_open_directory(const char *path); // Creates a NEW window
// Drag and Drop support
// This now needs to find WHICH explorer window is under the mouse
bool explorer_get_file_at(int screen_x, int screen_y, char *out_path, bool *is_dir);
void explorer_import_file(Window *win, const char *source_path); // To focused or default
void explorer_import_file_to(Window *win, const char *source_path, const char *dest_dir);
void explorer_refresh(Window *win);
void explorer_refresh_all(void);
void explorer_clear_click_state(Window *win);
// String Helpers
size_t explorer_strlen(const char *str);
void explorer_strcpy(char *dest, const char *src);
void explorer_strcat(char *dest, const char *src);
// Clipboard (System-wide)
void explorer_clipboard_copy(const char *path);
void explorer_clipboard_cut(const char *path);
void explorer_clipboard_paste(Window *win, const char *dest_dir);
bool explorer_clipboard_has_content(void);
// File Operations
bool explorer_delete_permanently(const char *path);
bool explorer_delete_recursive(const char *path);
void explorer_create_shortcut(Window *win, const char *target_path);
#endif

208
src/wm/font.h Normal file
View File

@@ -0,0 +1,208 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#ifndef FONT_H
#define FONT_H
#include <stdint.h>
// Minimal 8x8 font for ASCII 32-127
static const uint8_t font8x8_basic[128][8] = {
// 0-31 Control chars (empty)
{0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0},
{0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0},
// 32 Space
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
// 33 !
{0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x00},
// 34 "
{0x66, 0x66, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00},
// 35 #
{0x6C, 0x6C, 0xFE, 0x6C, 0xFE, 0x6C, 0x6C, 0x00},
// 36 $
{0x18, 0x3E, 0x60, 0x3C, 0x06, 0x7C, 0x18, 0x00},
// 37 %
{0x00, 0xC6, 0xCC, 0x18, 0x30, 0x66, 0xC6, 0x00},
// 38 &
{0x38, 0x6C, 0x38, 0x76, 0xDC, 0xCC, 0x76, 0x00},
// 39 '
{0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00},
// 40 (
{0x0C, 0x18, 0x30, 0x30, 0x30, 0x18, 0x0C, 0x00},
// 41 )
{0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x18, 0x30, 0x00},
// 42 *
{0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00},
// 43 +
{0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00},
// 44 ,
{0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30},
// 45 -
{0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00},
// 46 .
{0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00},
// 47 /
{0x00, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x00},
// 48 0
{0x3C, 0x66, 0x6E, 0x76, 0x66, 0x66, 0x3C, 0x00},
// 49 1
{0x18, 0x18, 0x38, 0x18, 0x18, 0x18, 0x7E, 0x00},
// 50 2
{0x3C, 0x66, 0x06, 0x0C, 0x30, 0x60, 0x7E, 0x00},
// 51 3
{0x3C, 0x66, 0x06, 0x1C, 0x06, 0x66, 0x3C, 0x00},
// 52 4
{0x06, 0x0E, 0x1E, 0x36, 0x66, 0x7F, 0x06, 0x00},
// 53 5
{0x7E, 0x60, 0x7C, 0x06, 0x06, 0x66, 0x3C, 0x00},
// 54 6
{0x3C, 0x66, 0x60, 0x7C, 0x66, 0x66, 0x3C, 0x00},
// 55 7
{0x7E, 0x06, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x00},
// 56 8
{0x3C, 0x66, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00},
// 57 9
{0x3C, 0x66, 0x66, 0x3E, 0x06, 0x66, 0x3C, 0x00},
// 58 :
{0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00},
// 59 ;
{0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x30},
// 60 <
{0x0C, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0C, 0x00},
// 61 =
{0x00, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x00, 0x00},
// 62 >
{0x30, 0x18, 0x0C, 0x06, 0x0C, 0x18, 0x30, 0x00},
// 63 ?
{0x3C, 0x66, 0x06, 0x0C, 0x18, 0x00, 0x18, 0x00},
// 64 @
{0x3C, 0x66, 0x6E, 0x6A, 0x68, 0x60, 0x3C, 0x00},
// 65 A
{0x18, 0x3C, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x00},
// 66 B
{0x7C, 0x66, 0x66, 0x7C, 0x66, 0x66, 0x7C, 0x00},
// 67 C
{0x3C, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3C, 0x00},
// 68 D
{0x78, 0x6C, 0x66, 0x66, 0x66, 0x6C, 0x78, 0x00},
// 69 E
{0x7E, 0x60, 0x60, 0x7C, 0x60, 0x60, 0x7E, 0x00},
// 70 F
{0x7E, 0x60, 0x60, 0x7C, 0x60, 0x60, 0x60, 0x00},
// 71 G
{0x3C, 0x66, 0x60, 0x6E, 0x66, 0x66, 0x3C, 0x00},
// 72 H
{0x66, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00},
// 73 I
{0x3C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00},
// 74 J
{0x06, 0x06, 0x06, 0x06, 0x06, 0x66, 0x3C, 0x00},
// 75 K
{0x66, 0x6C, 0x78, 0x70, 0x78, 0x6C, 0x66, 0x00},
// 76 L
{0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7E, 0x00},
// 77 M
{0x63, 0x77, 0x7F, 0x6B, 0x63, 0x63, 0x63, 0x00},
// 78 N
{0x66, 0x76, 0x7E, 0x7E, 0x6E, 0x66, 0x66, 0x00},
// 79 O
{0x3C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00},
// 80 P
{0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x60, 0x00},
// 81 Q
{0x3C, 0x66, 0x66, 0x66, 0x6A, 0x3C, 0x0E, 0x00},
// 82 R
{0x7C, 0x66, 0x66, 0x7C, 0x78, 0x6C, 0x66, 0x00},
// 83 S
{0x3C, 0x66, 0x60, 0x3C, 0x06, 0x66, 0x3C, 0x00},
// 84 T
{0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00},
// 85 U
{0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00},
// 86 V
{0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00},
// 87 W
{0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00},
// 88 X
{0x66, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x66, 0x00},
// 89 Y
{0x66, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x00},
// 90 Z
{0x7E, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x7E, 0x00},
// 91 [
{0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1E, 0x00},
// 92 backslash
{0x00, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x00},
// 93 ]
{0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00},
// 94 ^
{0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00},
// 95 _
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF},
// 96 `
{0x18, 0x0C, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00},
// 97 a
{0x00, 0x00, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00},
// 98 b
{0x60, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x7C, 0x00},
// 99 c
{0x00, 0x00, 0x3C, 0x66, 0x60, 0x66, 0x3C, 0x00},
// 100 d
{0x06, 0x06, 0x3E, 0x66, 0x66, 0x66, 0x3E, 0x00},
// 101 e
{0x00, 0x00, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00},
// 102 f
{0x0C, 0x18, 0x18, 0x3E, 0x18, 0x18, 0x18, 0x00},
// 103 g
{0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x3C},
// 104 h
{0x60, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x00},
// 105 i
{0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x3C, 0x00},
// 106 j
{0x06, 0x00, 0x06, 0x06, 0x06, 0x06, 0x3C, 0x00},
// 107 k
{0x60, 0x60, 0x66, 0x6C, 0x78, 0x6C, 0x66, 0x00},
// 108 l
{0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00},
// 109 m
{0x00, 0x00, 0x76, 0x7F, 0x6B, 0x6B, 0x63, 0x00},
// 110 n
{0x00, 0x00, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x00},
// 111 o
{0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00},
// 112 p
{0x00, 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60},
// 113 q
{0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x06},
// 114 r
{0x00, 0x00, 0x5C, 0x62, 0x60, 0x60, 0x60, 0x00},
// 115 s
{0x00, 0x00, 0x3C, 0x60, 0x3C, 0x06, 0x3C, 0x00},
// 116 t
{0x10, 0x10, 0x3E, 0x10, 0x10, 0x10, 0x0C, 0x00},
// 117 u
{0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00},
// 118 v
{0x00, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00},
// 119 w
{0x00, 0x00, 0x63, 0x6B, 0x7F, 0x3E, 0x36, 0x00},
// 120 x
{0x00, 0x00, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x00},
// 121 y
{0x00, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x06, 0x3C},
// 122 z
{0x00, 0x00, 0x7E, 0x0C, 0x18, 0x30, 0x7E, 0x00},
// 123 {
{0x0E, 0x18, 0x18, 0x70, 0x18, 0x18, 0x0E, 0x00},
// 124 |
{0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00},
// 125 }
{0x70, 0x18, 0x18, 0x0E, 0x18, 0x18, 0x70, 0x00},
// 126 ~
{0x76, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
// 127 DEL
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
};
#endif

227
src/wm/font_manager.c Normal file
View File

@@ -0,0 +1,227 @@
#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;
#define FONT_CACHE_SIZE 2048
typedef struct {
char c;
float pixel_height;
int w, h, xoff, yoff;
unsigned char *bitmap;
} font_cache_entry_t;
// Cache is disabled for now due to race conditions and collisions
// static font_cache_entry_t g_font_cache[FONT_CACHE_SIZE];
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->pixel_height = size;
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;
font_manager_render_char_scaled(font, x, y, c, color, font->pixel_height, put_pixel_fn);
}
void font_manager_render_char_scaled(ttf_font_t *font, int x, int y, char c, uint32_t color, float scale, void (*put_pixel_fn)(int, int, uint32_t)) {
if (!font) font = default_font;
if (!font) return;
stbtt_fontinfo *info = (stbtt_fontinfo *)font->info;
unsigned char *bitmap = NULL;
int w, h, xoff, yoff;
float real_scale = stbtt_ScaleForPixelHeight(info, scale); // Convert pixel size back to stbtt scale
int codepoint = (unsigned char)c;
if (codepoint == 128) codepoint = 0x2014; // &mdash; (—)
if (codepoint == 129) codepoint = 0x2013; // &ndash; ()
if (codepoint == 130) codepoint = 0x2022; // &bull; (•)
if (codepoint == 131) codepoint = 0x2026; // &hellip; (…)
if (codepoint == 132) codepoint = 0x2122; // &trade; (™)
if (codepoint == 133) codepoint = 0x20AC; // &euro; (€)
if (codepoint == 134) codepoint = 0x00B7; // &middot; (·)
bitmap = stbtt_GetCodepointBitmap(info, 0, real_scale, codepoint, &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) return 0;
return font_manager_get_string_width_scaled(font, s, font->pixel_height);
}
int font_manager_get_font_height_scaled(ttf_font_t *font, float scale) {
if (!font) font = default_font;
if (!font) return 0;
float real_scale = stbtt_ScaleForPixelHeight((stbtt_fontinfo *)font->info, scale);
return (int)((font->ascent - font->descent) * real_scale);
}
int font_manager_get_font_ascent_scaled(ttf_font_t *font, float scale) {
if (!font) font = default_font;
if (!font) return 0;
float real_scale = stbtt_ScaleForPixelHeight((stbtt_fontinfo *)font->info, scale);
return (int)(font->ascent * real_scale);
}
int font_manager_get_font_line_height_scaled(ttf_font_t *font, float scale) {
if (!font) font = default_font;
if (!font) return 0;
float real_scale = stbtt_ScaleForPixelHeight((stbtt_fontinfo *)font->info, scale);
return (int)((font->ascent - font->descent + font->line_gap) * real_scale);
}
int font_manager_get_string_width_scaled(ttf_font_t *font, const char *s, float scale) {
if (!font) font = default_font;
if (!font || !s) return 0;
stbtt_fontinfo *info = (stbtt_fontinfo *)font->info;
float real_scale = stbtt_ScaleForPixelHeight(info, scale);
int width = 0;
while (*s) {
int advance, lsb;
int codepoint = (unsigned char)*s;
if (codepoint == 128) codepoint = 0x2014; // &mdash; (—)
if (codepoint == 129) codepoint = 0x2013; // &ndash; ()
if (codepoint == 130) codepoint = 0x2022; // &bull; (•)
if (codepoint == 131) codepoint = 0x2026; // &hellip; (…)
if (codepoint == 132) codepoint = 0x2122; // &trade; (™)
if (codepoint == 133) codepoint = 0x20AC; // &euro; (€)
if (codepoint == 134) codepoint = 0x00B7; // &middot; (·)
stbtt_GetCodepointHMetrics(info, codepoint, &advance, &lsb);
// Round per-character to match draw_string's accumulation
width += (int)(advance * real_scale + 0.5f);
s++;
}
return width;
}

60
src/wm/font_manager.h Normal file
View File

@@ -0,0 +1,60 @@
#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;
float pixel_height;
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));
void font_manager_render_char_scaled(ttf_font_t *font, int x, int y, char c, uint32_t color, float scale, void (*put_pixel_fn)(int, int, uint32_t));
int font_manager_get_string_width(ttf_font_t *font, const char *s);
int font_manager_get_string_width_scaled(ttf_font_t *font, const char *s, float scale);
int font_manager_get_font_height_scaled(ttf_font_t *font, float scale);
int font_manager_get_font_ascent_scaled(ttf_font_t *font, float scale);
int font_manager_get_font_line_height_scaled(ttf_font_t *font, float scale);
#endif

728
src/wm/graphics.c Normal file
View File

@@ -0,0 +1,728 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include <stddef.h>
#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);
static int g_color_mode = 0;
#define PATTERN_SIZE 128
static uint32_t g_bg_pattern[PATTERN_SIZE * PATTERN_SIZE];
static bool g_use_pattern = false;
static uint32_t *g_bg_image = NULL;
static int g_bg_image_w = 0;
static int g_bg_image_h = 0;
static bool g_use_image = false;
static DirtyRect g_dirty = {0, 0, 0, 0, false};
#define MAX_FB_WIDTH 2048
#define MAX_FB_HEIGHT 2048
static uint32_t g_back_buffer[MAX_FB_WIDTH * MAX_FB_HEIGHT] __attribute__((aligned(4096)));
static int g_clip_x = 0, g_clip_y = 0, g_clip_w = 0, g_clip_h = 0;
static bool g_clip_enabled = false;
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;
// Initialize back buffer to black
for (int i = 0; i < MAX_FB_WIDTH * MAX_FB_HEIGHT; i++) {
g_back_buffer[i] = 0;
}
}
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_update_resolution(int width, int height, int bpp, void* fb_addr, int color_mode) {
if (!g_fb) return;
uint64_t rflags;
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
g_fb->width = width;
g_fb->height = height;
g_fb->bpp = bpp;
g_fb->pitch = width * (bpp / 8);
g_fb->address = fb_addr;
g_color_mode = color_mode;
// Clear back buffer
for (int i = 0; i < MAX_FB_WIDTH * MAX_FB_HEIGHT; i++) {
g_back_buffer[i] = 0;
}
// Clear dirty rect
g_dirty.active = false;
asm volatile("push %0; popfq" : : "r"(rflags));
}
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;
}
int get_screen_height(void) {
return g_fb ? g_fb->height : 0;
}
// Merge new dirty rect with existing one
static void merge_dirty_rect(int x, int y, int w, int h) {
if (!g_dirty.active) {
g_dirty.x = x;
g_dirty.y = y;
g_dirty.w = w;
g_dirty.h = h;
g_dirty.active = true;
} else {
// Calculate union of two rectangles
int x1 = g_dirty.x;
int y1 = g_dirty.y;
int x2 = g_dirty.x + g_dirty.w;
int y2 = g_dirty.y + g_dirty.h;
int new_x1 = x;
int new_y1 = y;
int new_x2 = x + w;
int new_y2 = y + h;
g_dirty.x = new_x1 < x1 ? new_x1 : x1;
g_dirty.y = new_y1 < y1 ? new_y1 : y1;
g_dirty.w = (new_x2 > x2 ? new_x2 : x2) - g_dirty.x;
g_dirty.h = (new_y2 > y2 ? new_y2 : y2) - g_dirty.y;
}
}
void graphics_mark_dirty(int x, int y, int w, int h) {
uint64_t rflags;
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
// Clamp to screen bounds
if (x < 0) {
w += x;
x = 0;
}
if (y < 0) {
h += y;
y = 0;
}
if (x + w > get_screen_width()) {
w = get_screen_width() - x;
}
if (y + h > get_screen_height()) {
h = get_screen_height() - y;
}
if (w <= 0 || h <= 0) {
asm volatile("push %0; popfq" : : "r"(rflags));
return;
}
merge_dirty_rect(x, y, w, h);
asm volatile("push %0; popfq" : : "r"(rflags));
}
void graphics_mark_screen_dirty(void) {
g_dirty.x = 0;
g_dirty.y = 0;
g_dirty.w = get_screen_width();
g_dirty.h = get_screen_height();
g_dirty.active = true;
}
DirtyRect graphics_get_dirty_rect(void) {
return g_dirty;
}
void graphics_clear_dirty(void) {
uint64_t rflags;
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
g_dirty.active = false;
asm volatile("push %0; popfq" : : "r"(rflags));
}
void graphics_set_render_target(uint32_t *buffer, int w, int h) {
g_render_target = buffer;
g_rt_width = w;
g_rt_height = 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) {
g_render_target[y * g_rt_width + x] = color;
}
return;
}
if (!g_fb) return;
if (x < 0 || x >= (int)g_fb->width || y < 0 || y >= (int)g_fb->height) return;
if (g_clip_enabled) {
if (x < g_clip_x || x >= g_clip_x + g_clip_w ||
y < g_clip_y || y >= g_clip_y + g_clip_h) {
return;
}
}
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;
if (g_render_target) {
if (x1 < 0) x1 = 0;
if (y1 < 0) y1 = 0;
if (x2 > g_rt_width) x2 = g_rt_width;
if (y2 > g_rt_height) y2 = g_rt_height;
if (x1 >= x2 || y1 >= y2) return;
for (int i = y1; i < y2; i++) {
uint32_t *row = &g_render_target[i * g_rt_width + x1];
int len = x2 - x1;
for (int j = 0; j < len; j++) {
row[j] = color;
}
}
return;
}
if (!g_fb) return;
if (g_clip_enabled) {
if (x1 < g_clip_x) x1 = g_clip_x;
if (y1 < g_clip_y) y1 = g_clip_y;
if (x2 > g_clip_x + g_clip_w) x2 = g_clip_x + g_clip_w;
if (y2 > g_clip_y + g_clip_h) y2 = g_clip_y + g_clip_h;
}
if (x1 < 0) x1 = 0;
if (y1 < 0) y1 = 0;
if (x2 > (int)g_fb->width) x2 = g_fb->width;
if (y2 > (int)g_fb->height) y2 = g_fb->height;
if (x1 >= x2 || y1 >= y2) return;
for (int i = y1; i < y2; i++) {
uint32_t *row = &g_back_buffer[i * g_fb->width + x1];
int len = x2 - x1;
for (int j = 0; j < len; j++) {
row[j] = color;
}
}
}
// Simple integer-based square root approximation
static int isqrt(int n) {
if (n < 0) return 0;
if (n == 0) return 0;
int x = n;
int y = (x + 1) / 2;
while (y < x) {
x = y;
y = (x + n / x) / 2;
}
return x;
}
// Draw rounded rectangle outline
void draw_rounded_rect(int x, int y, int w, int h, int radius, uint32_t color) {
if (radius > w / 2) radius = w / 2;
if (radius > h / 2) radius = h / 2;
if (radius < 1) radius = 1;
// Draw top and bottom edges
draw_rect(x + radius, y, w - 2*radius, 1, color);
draw_rect(x + radius, y + h - 1, w - 2*radius, 1, color);
// Draw left and right edges
draw_rect(x, y + radius, 1, h - 2*radius, color);
draw_rect(x + w - 1, y + radius, 1, h - 2*radius, color);
// Draw corner circles using integer approximation
for (int i = 0; i < radius; i++) {
int j = isqrt(radius*radius - i*i);
// Top-left corner
put_pixel(x + radius - i - 1, y + radius - j, color);
// Top-right corner
put_pixel(x + w - radius + i, y + radius - j, color);
// Bottom-left corner
put_pixel(x + radius - i - 1, y + h - radius + j - 1, color);
// Bottom-right corner
put_pixel(x + w - radius + i, y + h - radius + j - 1, color);
}
}
// Draw filled rounded rectangle
void draw_rounded_rect_filled(int x, int y, int w, int h, int radius, uint32_t color) {
if (radius > w / 2) radius = w / 2;
if (radius > h / 2) radius = h / 2;
if (radius < 1) radius = 1;
// Draw main rectangle body (center part without corners)
draw_rect(x + radius, y, w - 2*radius, h, color);
draw_rect(x, y + radius, radius, h - 2*radius, color);
draw_rect(x + w - radius, y + radius, radius, h - 2*radius, color);
for (int dy = 0; dy < radius; dy++) {
int dx_top = isqrt(radius*radius - (radius - dy) * (radius - dy));
int dx_bottom = isqrt(radius*radius - dy*dy);
draw_rect(x + radius - dx_top, y + dy, dx_top, 1, color);
draw_rect(x + w - radius, y + dy, dx_top, 1, color);
draw_rect(x + radius - dx_bottom, y + h - radius + dy, dx_bottom, 1, color);
draw_rect(x + w - radius, y + h - radius + dy, dx_bottom, 1, 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;
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);
}
}
}
}
// 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
}
int graphics_get_font_height_scaled(float scale) {
if (g_current_ttf) {
return font_manager_get_font_height_scaled(g_current_ttf, scale);
}
return 10; // Fallback bitmap height
}
int graphics_get_string_width_scaled(const char *s, float scale) {
if (g_current_ttf) {
return font_manager_get_string_width_scaled(g_current_ttf, s, scale);
}
int len = 0;
while (s && s[len]) len++;
return len * 8; // Fallback bitmap width
}
void draw_string(int x, int y, const char *s, uint32_t color) {
if (g_current_ttf) draw_string_scaled(x, y, s, color, g_current_ttf->pixel_height);
else draw_string_scaled(x, y, s, color, 15.0f);
}
void draw_string_scaled(int x, int y, const char *s, uint32_t color, float scale) {
if (!s) return;
int cur_x = x;
if (g_current_ttf) {
// We let the font manager handle the stbtt scale internally to avoid bringing stb_truetype into graphics.c
int baseline = y + font_manager_get_font_ascent_scaled(g_current_ttf, scale) - 2;
int line_height = font_manager_get_font_line_height_scaled(g_current_ttf, scale);
while (*s) {
if (*s == '\n') {
cur_x = x;
baseline += line_height;
} else {
font_manager_render_char_scaled(g_current_ttf, cur_x, baseline, *s, color, scale, 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_scaled(g_current_ttf, buf, scale);
}
s++;
}
return;
}
int cur_y = y;
while (*s) {
if (*s == '\n') {
cur_x = x;
cur_y += 10;
} else {
draw_char(cur_x, cur_y, *s, color);
cur_x += 8;
}
s++;
}
}
void draw_desktop_background(void) {
if (!g_fb) return;
if (g_use_image && g_bg_image) {
// Draw wallpaper image (stretch/scale to screen)
int x1 = 0, y1 = 0, x2 = g_fb->width, y2 = g_fb->height;
if (g_clip_enabled) {
x1 = g_clip_x; y1 = g_clip_y;
x2 = g_clip_x + g_clip_w; y2 = g_clip_y + g_clip_h;
}
for (int y = y1; y < y2; y++) {
int src_y = y * g_bg_image_h / (int)g_fb->height;
if (src_y >= g_bg_image_h) src_y = g_bg_image_h - 1;
uint32_t *row = &g_back_buffer[y * g_fb->width + x1];
for (int x = x1; x < x2; x++) {
int src_x = x * g_bg_image_w / (int)g_fb->width;
if (src_x >= g_bg_image_w) src_x = g_bg_image_w - 1;
*row++ = g_bg_image[src_y * g_bg_image_w + src_x];
}
}
} else if (g_use_pattern) {
// Optimized tiled pattern: only draw within the clipping/dirty rect
int x1 = 0, y1 = 0, x2 = g_fb->width, y2 = g_fb->height;
if (g_clip_enabled) {
x1 = g_clip_x; y1 = g_clip_y;
x2 = g_clip_x + g_clip_w; y2 = g_clip_y + g_clip_h;
}
for (int y = y1; y < y2; y++) {
uint32_t *row = &g_back_buffer[y * g_fb->width + x1];
int py = y % PATTERN_SIZE;
for (int x = x1; x < x2; x++) {
*row++ = g_bg_pattern[py * PATTERN_SIZE + (x % PATTERN_SIZE)];
}
}
} else {
// Draw solid color
draw_rect(0, 0, g_fb->width, g_fb->height, g_bg_color);
}
}
void graphics_set_bg_color(uint32_t color) {
g_bg_color = color;
g_use_pattern = false;
g_use_image = false;
}
void graphics_set_bg_pattern(const uint32_t *pattern) {
if (!pattern) return;
// Copy pattern to internal buffer
for (int i = 0; i < PATTERN_SIZE * PATTERN_SIZE; i++) {
g_bg_pattern[i] = pattern[i];
}
g_use_pattern = true;
g_use_image = false;
}
void graphics_set_bg_image(uint32_t *pixels, int w, int h) {
g_bg_image = pixels;
g_bg_image_w = w;
g_bg_image_h = h;
g_use_image = true;
g_use_pattern = false;
}
void draw_boredos_logo(int x, int y, int scale) {
// Width: 60, Height: 16
// 1: Magenta, 2: Blue, 3: Cyan, 4: White, 0: Deadspace
static const uint8_t boredos_bmp[] = {
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};
const int bmp_w = 60;
const int bmp_h = 16;
for (int r = 0; r < bmp_h; r++) {
for (int c = 0; c < bmp_w; c++) {
uint8_t p = boredos_bmp[r * bmp_w + c];
if (p == 0) continue;
uint32_t color = 0;
switch(p) {
case 1: color = 0xFFB589D6; break; // Magenta
case 2: color = 0xFF569CD6; break; // Blue
case 3: color = 0xFF4EC9B0; break; // Cyan
case 4: color = 0xFFFFFFFF; break; // White
}
draw_rect(x + (c * scale), y + (r * scale), scale, scale, color);
}
}
}
// Double buffering functions
void graphics_clear_back_buffer(uint32_t color) {
if (!g_fb) return;
uint32_t *buf = g_back_buffer;
for (int i = 0; i < (int)g_fb->width * (int)g_fb->height; i++) {
*buf++ = color;
}
}
void graphics_flip_buffer(void) {
if (!g_fb || !g_dirty.active) return;
int x = g_dirty.x;
int y = g_dirty.y;
int w = g_dirty.w;
int h = g_dirty.h;
if (x < 0) { w += x; x = 0; }
if (y < 0) { h += y; y = 0; }
if (x + w > (int)g_fb->width) w = g_fb->width - x;
if (y + h > (int)g_fb->height) h = g_fb->height - y;
if (w <= 0 || h <= 0) return;
for (int i = 0; i < h; i++) {
int curr_y = y + i;
uint32_t *src_row = &g_back_buffer[curr_y * g_fb->width + x];
if (g_fb->bpp == 32) {
uint32_t *dst_row = (uint32_t *)((uint8_t *)g_fb->address + curr_y * g_fb->pitch) + x;
for (int j = 0; j < w; j++) {
dst_row[j] = src_row[j];
}
} else if (g_fb->bpp == 16) {
uint16_t *dst_row = (uint16_t *)((uint8_t *)g_fb->address + curr_y * g_fb->pitch) + x;
for (int j = 0; j < w; j++) {
uint32_t c = src_row[j];
uint16_t r = ((c >> 16) & 0xFF) >> 3;
uint16_t g = ((c >> 8) & 0xFF) >> 2;
uint16_t b = (c & 0xFF) >> 3;
dst_row[j] = (r << 11) | (g << 5) | b;
}
} else if (g_fb->bpp == 8) {
uint8_t *dst_row = (uint8_t *)((uint8_t *)g_fb->address + curr_y * g_fb->pitch) + x;
if (g_color_mode == 1) { // Grayscale
for (int j = 0; j < w; j++) {
uint32_t c = src_row[j];
uint8_t r = (c >> 16) & 0xFF;
uint8_t g = (c >> 8) & 0xFF;
uint8_t b = c & 0xFF;
dst_row[j] = (uint8_t)((r * 77 + g * 150 + b * 29) >> 8);
}
} else if (g_color_mode == 2) { // Monochrome
static const uint8_t bayer2[2][2] = {
{ 0, 128 },
{192, 64 }
};
for (int j = 0; j < w; j++) {
uint32_t c = src_row[j];
uint8_t r = (c >> 16) & 0xFF;
uint8_t g = (c >> 8) & 0xFF;
uint8_t b = c & 0xFF;
int gray = (r * 77 + g * 150 + b * 29) >> 8;
// Boost contrast by 2x to separate the dark UI colors:
// Background (~30) -> 60
// Panel (~40) -> 80
// With thresholds {0, 64, 128, 192}:
// BG > 0 (1/4 white), Panel > 64 (2/4 white - checkerboard)
// Text (~170) -> 255 (solid white)
gray = gray * 2;
if (gray > 255) gray = 255;
int sx = x + j;
uint8_t threshold = bayer2[curr_y & 1][sx & 1];
dst_row[j] = (gray > threshold) ? 255 : 0;
}
} else { // 256 Colors (Standard)
for (int j = 0; j < w; j++) {
uint32_t c = src_row[j];
uint8_t r = ((c >> 16) & 0xFF) >> 5;
uint8_t g = ((c >> 8) & 0xFF) >> 5;
uint8_t b = (c & 0xFF) >> 6;
dst_row[j] = (r << 5) | (g << 2) | b;
}
}
}
}
}
void graphics_copy_screenbuffer(uint32_t *dest) {
if (!g_fb || !dest) return;
uint64_t rflags;
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
int sw = g_fb->width;
int sh = g_fb->height;
// Copy the internal back object to the dest directly
for (int y = 0; y < sh; y++) {
uint32_t *src_row = &g_back_buffer[y * sw];
for (int x = 0; x < sw; x++) {
dest[y * sw + x] = src_row[x];
}
}
asm volatile("push %0; popfq" : : "r"(rflags));
}
void graphics_set_clipping(int x, int y, int w, int h) {
if (x < 0) { w += x; x = 0; }
if (y < 0) { h += y; y = 0; }
int sw = get_screen_width();
int sh = get_screen_height();
if (x + w > sw) w = sw - x;
if (y + h > sh) h = sh - y;
if (w < 0) w = 0;
if (h < 0) h = 0;
g_clip_x = x;
g_clip_y = y;
g_clip_w = w;
g_clip_h = h;
g_clip_enabled = true;
}
void graphics_clear_clipping(void) {
g_clip_enabled = false;
}
void graphics_blit_buffer(uint32_t *src, int dst_x, int dst_y, int w, int h) {
if (!g_fb || !src) return;
int sw = get_screen_width();
int sh = get_screen_height();
for (int y = 0; y < h; y++) {
int vy = dst_y + y;
if (vy < 0 || vy >= sh) continue;
for (int x = 0; x < w; x++) {
int vx = dst_x + x;
if (vx < 0 || vx >= sw) continue;
uint32_t pcol = src[y * w + x];
if ((pcol & 0xFF000000) != 0 || (pcol & 0xFFFFFF) != 0) {
g_back_buffer[vy * sw + vx] = pcol;
}
}
}
}

66
src/wm/graphics.h Normal file
View File

@@ -0,0 +1,66 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#ifndef GRAPHICS_H
#define GRAPHICS_H
#include <stdint.h>
#include <stdbool.h>
#include "limine.h"
// Dirty rectangle structure
typedef struct {
int x, y, w, h;
bool active;
} 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_string_scaled(int x, int y, const char *s, uint32_t color, float scale);
void draw_desktop_background(void);
void graphics_set_bg_color(uint32_t color);
void graphics_set_bg_pattern(const uint32_t *pattern); // 128x128 pattern
void graphics_set_bg_image(uint32_t *pixels, int w, int h); // Full-screen wallpaper image
void graphics_set_render_target(uint32_t *buffer, int w, int h);
void graphics_blit_buffer(uint32_t *src, int dst_x, int dst_y, int w, int h);
void graphics_copy_screenbuffer(uint32_t *dest);
void draw_boredos_logo(int x, int y, int scale);
// Get screen dimensions
int get_screen_width(void);
int get_screen_height(void);
void graphics_update_resolution(int width, int height, int bpp, void* fb_addr, int color_mode);
// Dirty rectangle management
void graphics_mark_dirty(int x, int y, int w, int h);
void graphics_mark_screen_dirty(void);
DirtyRect graphics_get_dirty_rect(void);
void graphics_clear_dirty(void);
// Double buffering
void graphics_flip_buffer(void);
void graphics_clear_back_buffer(uint32_t color);
// Clipping
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);
int graphics_get_font_height_scaled(float scale);
int graphics_get_string_width_scaled(const char *s, float scale);
void graphics_set_font(const char *path);
#endif

41
src/wm/gui_ipc.h Normal file
View File

@@ -0,0 +1,41 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#ifndef GUI_IPC_H
#define GUI_IPC_H
#define GUI_CMD_WINDOW_CREATE 1
#define GUI_CMD_DRAW_RECT 2
#define GUI_CMD_DRAW_STRING 3
#define GUI_CMD_MARK_DIRTY 4
#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_WINDOW_SET_RESIZABLE 14
#define GUI_CMD_GET_SCREEN_SIZE 17
#define GUI_CMD_GET_SCREENBUFFER 18
#define GUI_CMD_SHOW_NOTIFICATION 19
#define GUI_CMD_GET_DATETIME 20
#define GUI_EVENT_NONE 0
#define GUI_EVENT_PAINT 1
#define GUI_EVENT_CLICK 2
#define GUI_EVENT_RIGHT_CLICK 3
#define GUI_EVENT_CLOSE 4
#define GUI_EVENT_KEY 5
#define GUI_EVENT_MOUSE_DOWN 6
#define GUI_EVENT_MOUSE_UP 7
#define GUI_EVENT_MOUSE_MOVE 8
#define GUI_EVENT_KEYUP 10
#define GUI_EVENT_RESIZE 11
typedef struct {
int type;
int arg1; // For click: x
int arg2; // For click: y
int arg3; // For click: button state
} gui_event_t;
#endif

29
src/wm/stb_image.c Normal file
View File

@@ -0,0 +1,29 @@
// stb_image.c wrapper for Kernel Mode
#include "memory_manager.h"
// Define custom memory allocators for stb_image in freestanding environment
#define STBI_MALLOC kmalloc
#define STBI_REALLOC krealloc
#define STBI_FREE kfree
// Kernel doesn't have standard I/O headers or functions
#define STBI_NO_STDIO
#define STBI_ASSERT(x) ((void)(x))
// Kernel doesn't have math.h, so disable float/hdr functions
#define STBI_NO_LINEAR
#define STBI_NO_HDR
// Limit maximum dimensions to prevent memory exhaustion and large stack frames
#define STBI_MAX_DIMENSIONS 4096
// Kernel doesn't have standard stdlib, disable SIMD and provide abs
#define STBI_NO_SIMD
#define STBI_NO_PSD
#define STBI_NO_PIC
#define STBI_NO_PNM
static inline int abs(int x) { return x < 0 ? -x : x; }
// Include the implementation
#define STB_IMAGE_IMPLEMENTATION
#include "userland/stb_image.h"

5079
src/wm/stb_truetype.h Normal file

File diff suppressed because it is too large Load Diff

84
src/wm/vga.c Normal file
View File

@@ -0,0 +1,84 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include "vga.h"
#include "io.h"
#include "pci.h"
#include "platform.h"
extern void serial_write(const char *str);
extern void serial_write_num(uint32_t n);
static void vga_write_register(uint16_t index, uint16_t data) {
outw(VBE_DISPI_IOPORT_INDEX, index);
outw(VBE_DISPI_IOPORT_DATA, data);
}
bool vga_set_mode(uint16_t width, uint16_t height, uint16_t bpp, void **out_framebuffer) {
// 1. Locate the BGA PCI device to get the physical framebuffer address
pci_device_t bga_dev;
// Standard VGA compatible controller is Class 0x03, Subclass 0x00
if (!pci_find_device_by_class(0x03, 0x00, &bga_dev)) {
serial_write("[VGA] Error: VGA compatible controller not found via PCI!\n");
return false;
}
// Read BAR0 (offset 0x10)
uint32_t bar0 = pci_read_config(bga_dev.bus, bga_dev.device, bga_dev.function, 0x10);
// physical address is bar0 with the lower 4 bits masked out
uint32_t phys_base = bar0 & 0xFFFFFFF0;
if (phys_base == 0) {
serial_write("[VGA] Error: Invalid BAR0 physical base.\n");
return false;
}
serial_write("[VGA] Found VGA Controller. LFB Phys Base: ");
serial_write_num(phys_base);
serial_write("\n");
// Map physical to virtual using p2v
void *vram_ptr = (void *)p2v(phys_base);
// 2. Disable BGA extensions first
vga_write_register(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED);
// 3. Set resolution and BPP
vga_write_register(VBE_DISPI_INDEX_XRES, width);
vga_write_register(VBE_DISPI_INDEX_YRES, height);
vga_write_register(VBE_DISPI_INDEX_BPP, bpp);
// 4. Re-enable BGA and Linear Framebuffer
vga_write_register(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED);
if (out_framebuffer) {
*out_framebuffer = vram_ptr;
}
return true;
}
void vga_set_palette_grayscale(void) {
outb(0x03C8, 0); // Palette index
for (int i = 0; i < 256; i++) {
// VGA palette uses 6 bits per channel (0-63)
uint8_t val = i >> 2;
outb(0x03C9, val); // R
outb(0x03C9, val); // G
outb(0x03C9, val); // B
}
}
void vga_set_palette_standard(void) {
outb(0x03C8, 0); // Palette index
for (int i = 0; i < 256; i++) {
// Standard 3:3:2 RGB mapping to 6-bit per channel
uint8_t r = (i >> 5) & 0x7;
uint8_t g = (i >> 2) & 0x7;
uint8_t b = i & 0x3;
outb(0x03C9, (r * 63) / 7);
outb(0x03C9, (g * 63) / 7);
outb(0x03C9, (b * 63) / 3);
}
}

36
src/wm/vga.h Normal file
View File

@@ -0,0 +1,36 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#ifndef VGA_H
#define VGA_H
#include <stdint.h>
#include <stdbool.h>
#define VBE_DISPI_IOPORT_INDEX 0x01CE
#define VBE_DISPI_IOPORT_DATA 0x01CF
#define VBE_DISPI_INDEX_ID 0
#define VBE_DISPI_INDEX_XRES 1
#define VBE_DISPI_INDEX_YRES 2
#define VBE_DISPI_INDEX_BPP 3
#define VBE_DISPI_INDEX_ENABLE 4
#define VBE_DISPI_INDEX_BANK 5
#define VBE_DISPI_INDEX_VIRT_WIDTH 6
#define VBE_DISPI_INDEX_VIRT_HEIGHT 7
#define VBE_DISPI_INDEX_X_OFFSET 8
#define VBE_DISPI_INDEX_Y_OFFSET 9
#define VBE_DISPI_DISABLED 0x00
#define VBE_DISPI_ENABLED 0x01
#define VBE_DISPI_LFB_ENABLED 0x40
#define COLOR_MODE_NORMAL 0
#define COLOR_MODE_GRAYSCALE 1
#define COLOR_MODE_MONOCHROME 2
bool vga_set_mode(uint16_t width, uint16_t height, uint16_t bpp, void **out_framebuffer);
void vga_set_palette_grayscale(void);
void vga_set_palette_standard(void);
#endif

159
src/wm/wallpaper.c Normal file
View File

@@ -0,0 +1,159 @@
#include "wallpaper.h"
#define STBI_NO_STDIO
#include "userland/stb_image.h"
#include "graphics.h"
#include "fat32.h"
#include "memory_manager.h"
#include "wm.h"
#include "io.h"
#include <stddef.h>
// Static buffer for the current wallpaper (max 1920x1080)
#define MAX_WP_WIDTH 1920
#define MAX_WP_HEIGHT 1080
static uint32_t* wp_pixels = NULL;
static int wp_width = 0;
static int wp_height = 0;
// Deferred wallpaper action (set from interrupt context, processed in main loop)
static volatile const char *pending_wallpaper_path = NULL;
static char pending_path_buf[256];
// Simple nearest-neighbor scale from decoded RGBA to ARGB pixel buffer
static void scale_rgba_to_argb(const unsigned char *rgba, int src_w, int src_h,
uint32_t *dst, int dst_w, int dst_h) {
for (int y = 0; y < dst_h; y++) {
int src_y = y * src_h / dst_h;
if (src_y >= src_h) src_y = src_h - 1;
for (int x = 0; x < dst_w; x++) {
int src_x = x * src_w / dst_w;
if (src_x >= src_w) src_x = src_w - 1;
size_t idx = ((size_t)src_y * (size_t)src_w + (size_t)src_x) * 4;
unsigned char r = rgba[idx];
unsigned char g = rgba[idx + 1];
unsigned char b = rgba[idx + 2];
unsigned char a = rgba[idx + 3];
dst[y * dst_w + x] = ((uint32_t)a << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
}
}
}
// Decode JPEG data from memory and set as wallpaper (MUST be called from non-interrupt context)
static int decode_and_set_wallpaper(const unsigned char *jpg_data, unsigned int jpg_size) {
int img_w, img_h, channels;
// We request 4 channels (RGBA) for better alignment and consistency with working userland code
unsigned char *rgba = stbi_load_from_memory(jpg_data, (int)jpg_size, &img_w, &img_h, &channels, 4);
if (!rgba || img_w <= 0 || img_h <= 0) {
return 0;
}
// Scale to screen size
int screen_w = get_screen_width();
int screen_h = get_screen_height();
if (screen_w > MAX_WP_WIDTH) screen_w = MAX_WP_WIDTH;
if (screen_h > MAX_WP_HEIGHT) screen_h = MAX_WP_HEIGHT;
if (!wp_pixels) {
wp_pixels = (uint32_t*)kmalloc(MAX_WP_WIDTH * MAX_WP_HEIGHT * sizeof(uint32_t));
if (!wp_pixels) {
stbi_image_free(rgba);
return 0;
}
}
scale_rgba_to_argb(rgba, img_w, img_h, wp_pixels, screen_w, screen_h);
wp_width = screen_w;
wp_height = screen_h;
stbi_image_free(rgba);
graphics_set_bg_image(wp_pixels, wp_width, wp_height);
return 1;
}
// Simple serial output for debugging (COM1 = 0x3F8)
static void serial_char(char c) {
while (!(inb(0x3F8 + 5) & 0x20)); // Wait for transmit ready
outb(0x3F8, c);
}
static void serial_str(const char *s) {
while (*s) serial_char(*s++);
}
static void serial_num(int n) {
if (n < 0) { serial_char('-'); n = -n; }
if (n >= 10) serial_num(n / 10);
serial_char('0' + (n % 10));
}
// Request wallpaper change by file path (safe to call from interrupt context)
void wallpaper_request_set_from_file(const char *path) {
// Copy path to buffer
int i = 0;
while (path[i] && i < 255) {
pending_path_buf[i] = path[i];
i++;
}
pending_path_buf[i] = 0;
pending_wallpaper_path = pending_path_buf;
}
// Process deferred wallpaper actions (called from main loop, NOT interrupt context)
void wallpaper_process_pending(void) {
if (pending_wallpaper_path) {
const char *path = (const char *)pending_wallpaper_path;
pending_wallpaper_path = NULL;
serial_str("[WP] Processing wallpaper: ");
serial_str(path);
serial_str("\n");
// Read file from filesystem
FAT32_FileHandle *fh = fat32_open(path, "r");
if (fh) {
uint32_t file_size = fh->size;
if (file_size > 0 && file_size <= 4 * 1024 * 1024) {
// Add padding to avoid stb_image reading past the end and potential corruption
size_t padded_size = file_size + 128;
unsigned char *buf = (unsigned char*)kmalloc(padded_size);
if (buf) {
mem_memset(buf, 0, padded_size);
int total_read = 0;
while (total_read < (int)file_size) {
int chunk = fat32_read(fh, buf + total_read, (int)file_size - total_read);
if (chunk <= 0) break;
total_read += chunk;
}
fat32_close(fh);
if (total_read > 0) {
decode_and_set_wallpaper(buf, (unsigned int)total_read);
wm_refresh();
}
kfree(buf);
} else {
fat32_close(fh);
}
} else {
fat32_close(fh);
}
}
}
}
uint32_t* wallpaper_get_pixels(void) { return wp_pixels; }
int wallpaper_get_width(void) { return wp_width; }
int wallpaper_get_height(void) { return wp_height; }
void wallpaper_init(void) {
// We expect Limine modules to have been copied to /Library/images/Wallpapers/ by main.c
// Set a default wallpaper if one exists
if (fat32_exists("/Library/images/Wallpapers/mountain.jpg")) {
wallpaper_request_set_from_file("/Library/images/Wallpapers/mountain.jpg");
} else if (fat32_exists("/Library/images/Wallpapers/moon.jpg")) {
wallpaper_request_set_from_file("/Library/images/Wallpapers/moon.jpg");
}
}

25
src/wm/wallpaper.h Normal file
View File

@@ -0,0 +1,25 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
// wallpaper.h - Wallpaper management for BoredOS
#ifndef WALLPAPER_H
#define WALLPAPER_H
#include <stdint.h>
#include <stdbool.h>
// Initialize wallpaper subsystem
void wallpaper_init(void);
// Request wallpaper change by file path (safe from interrupt context)
void wallpaper_request_set_from_file(const char *path);
// Process pending wallpaper actions (call from main loop only!)
void wallpaper_process_pending(void);
// Get decoded wallpaper pixel buffer
uint32_t* wallpaper_get_pixels(void);
int wallpaper_get_width(void);
int wallpaper_get_height(void);
#endif // WALLPAPER_H

2682
src/wm/wm.c Normal file

File diff suppressed because it is too large Load Diff

125
src/wm/wm.h Normal file
View File

@@ -0,0 +1,125 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#ifndef WM_H
#define WM_H
#include <stdint.h>
#include <stdbool.h>
// --- Constants ---
#define COLOR_TEAL 0xFF008080
#define COLOR_GRAY 0xFFC0C0C0
#define COLOR_WHITE 0xFFFFFFFF
#define COLOR_BLACK 0xFF000000
#define COLOR_BLUE 0xFF000080
#define COLOR_LTGRAY 0xFFDFDFDF
#define COLOR_DKGRAY 0xFF808080
#define COLOR_RED 0xFFFF0000
#define COLOR_PURPLE 0xFF800080
#define COLOR_COFFEE 0xFF6B4423
#define COLOR_APPLE_RED 0xFFFF0000
#define COLOR_APPLE_ORANGE 0xFFFF7F00
#define COLOR_APPLE_YELLOW 0xFFFFFF00
#define COLOR_APPLE_GREEN 0xFF00FF00
#define COLOR_APPLE_BLUE 0xFF0000FF
#define COLOR_APPLE_INDIGO 0xFF4B0082
#define COLOR_APPLE_VIOLET 0xFF9400D3
#define COLOR_NOTEPAD_BG 0xFFF5F5DC
#define COLOR_DARK_BG 0xFF1E1E1E // Main dark background
#define COLOR_DARK_PANEL 0xFF2D2D2D // Slightly lighter panel background
#define COLOR_DARK_TITLEBAR 0xFF282828 // Darker for title bar
#define COLOR_DARK_TEXT 0xFFF0F0F0 // Light gray text
#define COLOR_DARK_BORDER 0xFF3A3A3A // Border color
#define COLOR_DOCK_BG 0xFF3A3A3A // Dock background
#define COLOR_TOPBAR_BG 0xFF1A1A1A // Top bar background
#define COLOR_TRAFFIC_RED 0xFFED6158 // Close button red
#define COLOR_TRAFFIC_YELLOW 0xFFFCC02E // Minimize button (not used for now)
#define COLOR_TRAFFIC_GREEN 0xFF5FC038 // Zoom button (not used for now)
#define DESKTOP_TOP_DEADSPACE_HEIGHT 80 // stops files from being rendered under menu bar
typedef struct Window Window;
struct Window {
char *title;
int x, y, w, h;
bool visible;
char buffer[1024];
int buf_len;
int cursor_pos;
bool focused;
int z_index;
void *data;
uint32_t *pixels;
uint32_t *comp_pixels;
void *font;
// Callbacks
void (*paint)(Window *win);
void (*handle_key)(Window *win, char c, bool pressed);
void (*handle_click)(Window *win, int x, int y);
void (*handle_right_click)(Window *win, int x, int y);
void (*handle_close)(Window *win);
void (*handle_resize)(Window *win, int w, int h);
bool resizable;
};
void wm_init(void);
void wm_handle_mouse(int dx, int dy, uint8_t buttons, int dz);
void wm_handle_key(char c, bool pressed);
void wm_handle_click(int x, int y);
void wm_handle_right_click(int x, int y);
void wm_process_input(void);
void wm_process_deferred_thumbs(void);
void wm_add_window(Window *win);
void wm_remove_window(Window *win);
void wm_bring_to_front(Window *win);
Window* wm_find_window_by_title(const char *title);
// Redraw system
void wm_mark_dirty(int x, int y, int w, int h);
void wm_refresh(void);
void wm_paint(void);
void wm_refresh_desktop(void);
void wm_timer_tick(void);
uint32_t wm_get_ticks(void);
int wm_get_desktop_icon_count(void);
void wm_show_message(const char *title, const char *message);
void wm_notify_fs_change(void);
// Hook for external rendering (e.g. VM overlay)
extern void (*wm_custom_paint_hook)(void);
// Drawing helpers
void draw_bevel_rect(int x, int y, int w, int h, bool sunken);
void draw_button(int x, int y, int w, int h, const char *text, bool pressed);
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_traffic_light(int x, int y);
void draw_icon(int x, int y, const char *label);
void draw_folder_icon(int x, int y, const char *label);
void draw_document_icon(int x, int y, const char *label);
void draw_elf_icon(int x, int y, const char *label);
void draw_image_icon(int x, int y, const char *label);
void draw_notepad_icon(int x, int y, const char *label);
void draw_calculator_icon(int x, int y, const char *label);
void draw_terminal_icon(int x, int y, const char *label);
void draw_minesweeper_icon(int x, int y, const char *label);
void draw_control_panel_icon(int x, int y, const char *label);
void draw_about_icon(int x, int y, const char *label);
void draw_recycle_bin_icon(int x, int y, const char *label);
void draw_paint_icon(int x, int y, const char *label);
void draw_squircle_icon(int x, int y, const char *label, uint32_t bg_color);
void draw_files_icon(int x, int y, const char *label);
void draw_settings_icon(int x, int y, const char *label);
// Desktop Settings
extern bool desktop_snap_to_grid;
extern bool desktop_auto_align;
extern int desktop_max_rows_per_col;
extern int desktop_max_cols;
// Mouse Settings
extern int mouse_speed;
#endif