mirror of
https://github.com/JannisHeydemann/BoredOS.git
synced 2026-05-30 02:16:58 +00:00
src/kernel --> src/
This commit is contained in:
1870
src/wm/explorer.c
Normal file
1870
src/wm/explorer.c
Normal file
File diff suppressed because it is too large
Load Diff
88
src/wm/explorer.h
Normal file
88
src/wm/explorer.h
Normal 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
208
src/wm/font.h
Normal 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
227
src/wm/font_manager.c
Normal 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; // — (—)
|
||||
if (codepoint == 129) codepoint = 0x2013; // – (–)
|
||||
if (codepoint == 130) codepoint = 0x2022; // • (•)
|
||||
if (codepoint == 131) codepoint = 0x2026; // … (…)
|
||||
if (codepoint == 132) codepoint = 0x2122; // ™ (™)
|
||||
if (codepoint == 133) codepoint = 0x20AC; // € (€)
|
||||
if (codepoint == 134) codepoint = 0x00B7; // · (·)
|
||||
|
||||
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; // — (—)
|
||||
if (codepoint == 129) codepoint = 0x2013; // – (–)
|
||||
if (codepoint == 130) codepoint = 0x2022; // • (•)
|
||||
if (codepoint == 131) codepoint = 0x2026; // … (…)
|
||||
if (codepoint == 132) codepoint = 0x2122; // ™ (™)
|
||||
if (codepoint == 133) codepoint = 0x20AC; // € (€)
|
||||
if (codepoint == 134) codepoint = 0x00B7; // · (·)
|
||||
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
60
src/wm/font_manager.h
Normal 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
728
src/wm/graphics.c
Normal 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
66
src/wm/graphics.h
Normal 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
41
src/wm/gui_ipc.h
Normal 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
29
src/wm/stb_image.c
Normal 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
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
84
src/wm/vga.c
Normal 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
36
src/wm/vga.h
Normal 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
159
src/wm/wallpaper.c
Normal 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
25
src/wm/wallpaper.h
Normal 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
2682
src/wm/wm.c
Normal file
File diff suppressed because it is too large
Load Diff
125
src/wm/wm.h
Normal file
125
src/wm/wm.h
Normal 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
|
||||
Reference in New Issue
Block a user