OPT: Multithreaded WM rendering

This commit is contained in:
boreddevnl 2026-04-02 21:36:00 +02:00
parent e60f232812
commit 91b67bd8d5
7 changed files with 567 additions and 461 deletions

View file

@ -9,17 +9,16 @@
#include "platform.h"
#include "paging.h"
#include "process.h"
#include "work_queue.h"
extern void serial_write(const char *str);
extern void serial_write_num(uint32_t n);
extern void serial_write_hex(uint64_t n);
// --- Dynamically allocated per-CPU state ---
static cpu_state_t *cpu_states = NULL; // Array[cpu_count]
static cpu_state_t *cpu_states = NULL;
static uint32_t total_cpus = 0;
static uint32_t bsp_lapic_id = 0;
// Get LAPIC ID via CPUID leaf 0x01 (works on all x86_64)
static uint32_t read_lapic_id(void) {
uint32_t eax, ebx, ecx, edx;
asm volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(1));
@ -44,49 +43,37 @@ cpu_state_t *smp_get_cpu(uint32_t cpu_id) {
return &cpu_states[cpu_id];
}
// --- AP Entry Point ---
// Called by Limine on each Application Processor.
// The limine_smp_info* is passed as a parameter.
static void ap_entry(struct limine_smp_info *info) {
// 1. Figure out which CPU we are
uint32_t my_id = (uint32_t)(info->extra_argument);
// 2. Enable FPU/SSE on this core (same as BSP does in platform_init)
uint64_t cr0;
asm volatile("mov %%cr0, %0" : "=r"(cr0));
cr0 &= ~(1ULL << 2); // Clear EM
cr0 |= (1ULL << 1); // Set MP
cr0 |= (1ULL << 5); // Set NE
cr0 &= ~(1ULL << 2);
cr0 |= (1ULL << 1);
cr0 |= (1ULL << 5);
asm volatile("mov %0, %%cr0" : : "r"(cr0));
uint64_t cr4;
asm volatile("mov %%cr4, %0" : "=r"(cr4));
cr4 |= (1ULL << 9); // OSFXSR
cr4 |= (1ULL << 10); // OSXMMEXCPT
cr4 |= (1ULL << 9);
cr4 |= (1ULL << 10);
asm volatile("mov %0, %%cr4" : : "r"(cr4));
asm volatile("fninit");
// 3. Load the shared GDT and properly reload all segments (including CS=0x08)
extern struct gdt_ptr gdtr;
extern void gdt_flush(uint64_t);
gdt_flush((uint64_t)&gdtr);
// 4. Load per-CPU TSS
gdt_load_ap_tss(my_id);
// 5. Load the shared IDT
extern void idt_load(void);
idt_load();
// 6. Load the kernel page tables (same CR3 as BSP — shared kernel space)
uint64_t kernel_cr3 = paging_get_pml4_phys();
asm volatile("mov %0, %%cr3" : : "r"(kernel_cr3));
// 7. Enable LAPIC on this core so it can receive IPIs
extern void lapic_enable(void);
lapic_enable();
// 8. Mark ourselves as online
cpu_states[my_id].online = true;
serial_write("[SMP] AP ");
@ -95,25 +82,17 @@ static void ap_entry(struct limine_smp_info *info) {
serial_write_num(cpu_states[my_id].lapic_id);
serial_write(")\n");
// 9. Initialize the current_process pointer for this CPU
// Create a dedicated idle task for this AP (PID 0 is reserved for the BSP)
process_t *ap_idle = process_create(NULL, false); // Idle process
process_t *ap_idle = process_create(NULL, false);
ap_idle->cpu_affinity = my_id;
process_set_current_for_cpu(my_id, ap_idle);
// 10. Enable interrupts and enter idle halt loop.
// APs will be woken by scheduling IPIs from BSP (vector 0x41).
// The IPI handler does context switching for this CPU's processes.
asm volatile("sti");
// Idle loop — APs halt and wait for IPI
for (;;) { asm volatile("hlt"); }
work_queue_drain_loop();
}
// --- SMP Initialization ---
uint32_t smp_init(struct limine_smp_response *smp_resp) {
if (!smp_resp || smp_resp->cpu_count <= 1) {
// Single CPU system — just set up the BSP entry
total_cpus = 1;
cpu_states = (cpu_state_t *)kmalloc(sizeof(cpu_state_t));
if (!cpu_states) return 1;
@ -135,7 +114,6 @@ uint32_t smp_init(struct limine_smp_response *smp_resp) {
serial_write_num(bsp_lapic_id);
serial_write("\n");
// Allocate per-CPU state array
cpu_states = (cpu_state_t *)kmalloc(total_cpus * sizeof(cpu_state_t));
if (!cpu_states) {
serial_write("[SMP] ERROR: Failed to allocate CPU state array!\n");
@ -145,10 +123,8 @@ uint32_t smp_init(struct limine_smp_response *smp_resp) {
extern void mem_memset(void *, int, size_t);
mem_memset(cpu_states, 0, total_cpus * sizeof(cpu_state_t));
// Initialize per-CPU GDT/TSS entries for all CPUs
gdt_init_ap_tss(total_cpus);
// Fill in CPU state and start APs
uint32_t bsp_index = 0;
for (uint32_t i = 0; i < total_cpus; i++) {
struct limine_smp_info *cpu = smp_resp->cpus[i];
@ -156,7 +132,6 @@ uint32_t smp_init(struct limine_smp_response *smp_resp) {
cpu_states[i].lapic_id = cpu->lapic_id;
if (cpu->lapic_id == bsp_lapic_id) {
// This is the BSP — already running
cpu_states[i].online = true;
bsp_index = i;
serial_write("[SMP] BSP CPU ");
@ -165,7 +140,6 @@ uint32_t smp_init(struct limine_smp_response *smp_resp) {
serial_write_num(cpu->lapic_id);
serial_write(") online\n");
} else {
// Allocate a kernel stack for this AP
void *ap_stack = kmalloc_aligned(65536, 65536);
if (!ap_stack) {
serial_write("[SMP] ERROR: Failed to allocate AP stack!\n");
@ -175,27 +149,18 @@ uint32_t smp_init(struct limine_smp_response *smp_resp) {
cpu_states[i].kernel_stack_alloc = ap_stack;
cpu_states[i].online = false;
// Set extra_argument so the AP knows its index
cpu->extra_argument = i;
// Tell Limine to start this AP. Limine sets up the AP's stack
// from extra_argument's stack, but we need the goto_address.
// Limine will jump to ap_entry with the AP's limine_smp_info*.
// Important: Limine creates a temporary stack for the AP, and the
// goto_address is where the AP starts executing.
serial_write("[SMP] Starting AP ");
serial_write_num(i);
serial_write(" (LAPIC ");
serial_write_num(cpu->lapic_id);
serial_write(")...\n");
// This atomic write triggers the AP to start executing at ap_entry
__atomic_store_n(&cpu->goto_address, ap_entry, __ATOMIC_SEQ_CST);
}
}
// Wait for all APs to come online (with timeout)
volatile uint32_t timeout = 10000000;
uint32_t online_count = 0;
while (timeout-- > 0) {

View file

@ -9,6 +9,8 @@
#include "wm.h"
#include "fat32.h"
#include "paging.h"
#include "work_queue.h"
#include "smp.h"
#include "platform.h"
#include "io.h"
#include "pci.h"
@ -630,9 +632,10 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
if (win->pixels) {
int rx = (int)params[0]; int ry = (int)params[1];
int rw = (int)params[2]; int rh = (int)params[3];
int src_w = rw;
int src_x_offset = 0;
int src_y_offset = 0;
if (rx < 0) { src_x_offset = -rx; rw += rx; rx = 0; }
if (ry < 0) { src_y_offset = -ry; rh += ry; ry = 0; }
if (rx + rw > win->w) rw = win->w - rx;
@ -641,15 +644,13 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
if (rw > 0 && rh > 0) {
for (int y = 0; y < rh; y++) {
uint32_t *dest = &win->pixels[(ry + y) * win->w + rx];
uint32_t *src = &image_data[(src_y_offset + y) * (int)params[2] + src_x_offset];
uint32_t *src = &image_data[(src_y_offset + y) * src_w + src_x_offset];
for (int x = 0; x < rw; x++) {
uint32_t s = src[x];
uint8_t alpha = (s >> 24) & 0xFF;
if (alpha == 0xFF) {
dest[x] = s;
} else if (alpha == 0) {
// Skip
} else {
} else if (alpha > 0) {
uint32_t d = dest[x];
uint32_t rb = ((s & 0xFF00FF) * alpha + (d & 0xFF00FF) * (255 - alpha)) >> 8;
uint32_t g = ((s & 0x00FF00) * alpha + (d & 0x00FF00) * (255 - alpha)) >> 8;

View file

@ -8,6 +8,9 @@
#include <stdbool.h>
#include "stdlib.h"
// External syscalls from libc
extern void sys_parallel_run(void (*fn)(void*), void **args, int count);
// =========
// Constants
// =========
@ -464,6 +467,10 @@ static double surf_z2[GRID_3D][GRID_3D]; // Back root
static bool surf_v1[GRID_3D][GRID_3D];
static bool surf_v2[GRID_3D][GRID_3D];
// Screen-space vertex cache for parallel rasterization
static int surf_sx1[GRID_3D][GRID_3D], surf_sy1[GRID_3D][GRID_3D];
static int surf_sx2[GRID_3D][GRID_3D], surf_sy2[GRID_3D][GRID_3D];
static bool surface_needs_eval = true;
// Cached trig values for performance
@ -500,7 +507,9 @@ static widget_context_t wctx = { 0, gfx_draw_rect, gfx_draw_rr, gfx_draw_str, NU
// ================
static void gfb_clear(uint32_t c) {
int total = graph_w * graph_h;
for (int i = 0; i < total; i++) graph_fb[i] = c;
uint32_t *p = graph_fb;
// Simple but often faster for compilers to vectorize than raw loop
for (int i = 0; i < total; i++) p[i] = c;
}
static void gfb_pixel(int x, int y, uint32_t c) {
@ -865,6 +874,7 @@ typedef struct {
int start_j, end_j;
double range;
double step;
double z_scale;
} eval_job_t;
static void eval_3d_explicit_job(void *arg) {
@ -923,73 +933,81 @@ static void eval_3d_implicit_job(void *arg) {
}
}
static void render_3d_explicit(void) {
double step = range_3d * 2.0 / (GRID_3D - 1);
double zmin = 1e30, zmax = -1e30;
// why are you reading this lol
if (surface_needs_eval) {
int num_chunks = 4; // Parallelize into 4 chunks (matching typical core count)
eval_job_t jobs[4];
void *job_args[4];
int rows_per_chunk = GRID_3D / num_chunks;
for (int c = 0; c < num_chunks; c++) {
jobs[c].start_j = c * rows_per_chunk;
jobs[c].end_j = (c == num_chunks - 1) ? GRID_3D : (c + 1) * rows_per_chunk;
jobs[c].range = range_3d;
jobs[c].step = step;
job_args[c] = &jobs[c];
}
extern void sys_parallel_run(void (*fn)(void*), void **args, int count);
sys_parallel_run(eval_3d_explicit_job, job_args, num_chunks);
}
// Compute min/max for coloring based on what's visible
for (int j = 0; j < GRID_3D; j++) {
// Parallel Projection Job
static void eval_3d_project_job(void *arg) {
eval_job_t *job = (eval_job_t *)arg;
for (int j = job->start_j; j < job->end_j; j++) {
for (int i = 0; i < GRID_3D; i++) {
if (surf_v1[j][i]) {
if (surf_z1[j][i] < zmin) zmin = surf_z1[j][i];
if (surf_z1[j][i] > zmax) zmax = surf_z1[j][i];
project_3d(surf_x[j][i], surf_z1[j][i] * job->z_scale, surf_y_3d[j][i], &surf_sx1[j][i], &surf_sy1[j][i]);
}
}
}
double z_scale = 1.0;
if (zmax > zmin && (zmax - zmin) > 0.001) {
// Auto-fit Z bounds to X/Y bounds to prevent vertical spikes in explicit graphing
z_scale = (range_3d * 2.0) / (zmax - zmin);
}
// Draw wireframe
for (int j = 0; j < GRID_3D; j++) {
for (int i = 0; i < GRID_3D; i++) {
if (!surf_v1[j][i]) continue;
int sx0, sy0;
project_3d(surf_x[j][i], surf_z1[j][i] * z_scale, surf_y_3d[j][i], &sx0, &sy0);
uint32_t col = color_by_height(surf_z1[j][i], zmin, zmax);
if (i + 1 < GRID_3D && surf_v1[j][i+1]) {
int sx1, sy1;
project_3d(surf_x[j][i+1], surf_z1[j][i+1] * z_scale, surf_y_3d[j][i+1], &sx1, &sy1);
gfb_line(sx0, sy0, sx1, sy1, col);
}
if (j + 1 < GRID_3D && surf_v1[j+1][i]) {
int sx1, sy1;
project_3d(surf_x[j+1][i], surf_z1[j+1][i] * z_scale, surf_y_3d[j+1][i], &sx1, &sy1);
gfb_line(sx0, sy0, sx1, sy1, col);
if (surf_v2[j][i]) {
project_3d(surf_x[j][i], surf_z2[j][i] * job->z_scale, surf_y_3d[j][i], &surf_sx2[j][i], &surf_sy2[j][i]);
}
}
}
}
static void render_3d_implicit(void) {
typedef struct {
int start_j, end_j;
double zmin, zmax;
} draw_job_t;
// Parallel Draw Job
static void render_3d_draw_job(void *arg) {
draw_job_t *job = (draw_job_t *)arg;
for (int j = job->start_j; j < job->end_j; j++) {
for (int i = 0; i < GRID_3D; i++) {
for (int s = 0; s < 2; s++) {
bool *v = (s == 0) ? surf_v1[j] : surf_v2[j];
int *sx_row = (s == 0) ? surf_sx1[j] : surf_sx2[j];
int *sy_row = (s == 0) ? surf_sy1[j] : surf_sy2[j];
double *z_row = (s == 0) ? surf_z1[j] : surf_z2[j];
if (!v[i]) continue;
int sx0 = sx_row[i], sy0 = sy_row[i];
uint32_t col = color_by_height(z_row[i], job->zmin, job->zmax);
if (i + 1 < GRID_3D) {
bool v_next = (s == 0) ? surf_v1[j][i+1] : surf_v2[j][i+1];
if (v_next) {
int *sx_next_row = (s == 0) ? surf_sx1[j] : surf_sx2[j];
int *sy_next_row = (s == 0) ? surf_sy1[j] : surf_sy2[j];
gfb_line(sx0, sy0, sx_next_row[i+1], sy_next_row[i+1], col);
}
}
if (j + 1 < GRID_3D) {
bool v_next = (s == 0) ? surf_v1[j+1][i] : surf_v2[j+1][i];
if (v_next) {
int *sx_next_row = (s == 0) ? surf_sx1[j+1] : surf_sx2[j+1];
int *sy_next_row = (s == 0) ? surf_sy1[j+1] : surf_sy2[j+1];
gfb_line(sx0, sy0, sx_next_row[i], sy_next_row[i], col);
}
}
if (s == 0 && surf_v1[j][i] && surf_v2[j][i]) {
bool edge = false;
if (i+1 < GRID_3D && !surf_v1[j][i+1]) edge = true;
if (i-1 >= 0 && !surf_v1[j][i-1]) edge = true;
if (j+1 < GRID_3D && !surf_v1[j+1][i]) edge = true;
if (j-1 >= 0 && !surf_v1[j-1][i]) edge = true;
if (edge) {
gfb_line(sx0, sy0, surf_sx2[j][i], surf_sy2[j][i], col);
}
}
}
}
}
}
static void render_3d_explicit(void) {
double step = range_3d * 2.0 / (GRID_3D - 1);
int z_steps = 100;
double z_step = range_3d * 2.0 / z_steps;
double zmin = 1e30, zmax = -1e30;
if (surface_needs_eval) {
for (int j = 0; j < GRID_3D; j++) { for (int i = 0; i < GRID_3D; i++) { surf_v1[j][i] = false; surf_v2[j][i] = false; } }
int num_chunks = 4;
eval_job_t jobs[4];
void *job_args[4];
@ -1003,10 +1021,99 @@ static void render_3d_implicit(void) {
job_args[c] = &jobs[c];
}
extern void sys_parallel_run(void (*fn)(void*), void **args, int count);
sys_parallel_run(eval_3d_explicit_job, job_args, num_chunks);
}
// Compute min/max for coloring
for (int j = 0; j < GRID_3D; j++) {
for (int i = 0; i < GRID_3D; i++) {
if (surf_v1[j][i]) {
if (surf_z1[j][i] < zmin) zmin = surf_z1[j][i];
if (surf_z1[j][i] > zmax) zmax = surf_z1[j][i];
}
}
}
double z_scale = 1.0;
if (zmax > zmin && (zmax - zmin) > 0.001) {
z_scale = (range_3d * 2.0) / (zmax - zmin);
}
// Pass 2: Parallel Projection
{
int num_chunks = 4;
eval_job_t jobs[4];
void *job_args[4];
int rows_per_chunk = GRID_3D / num_chunks;
for (int c = 0; c < num_chunks; c++) {
jobs[c].start_j = c * rows_per_chunk;
jobs[c].end_j = (c == num_chunks - 1) ? GRID_3D : (c + 1) * rows_per_chunk;
jobs[c].range = range_3d;
jobs[c].step = step;
jobs[c].z_scale = z_scale;
job_args[c] = &jobs[c];
}
sys_parallel_run(eval_3d_project_job, job_args, num_chunks);
}
// Pass 3: Parallel Drawing
{
int num_chunks = 4;
draw_job_t jobs[4];
void *job_args[4];
int rows_per_chunk = GRID_3D / num_chunks;
for (int c = 0; c < num_chunks; c++) {
jobs[c].start_j = c * rows_per_chunk;
jobs[c].end_j = (c == num_chunks - 1) ? GRID_3D : (c + 1) * rows_per_chunk;
jobs[c].zmin = zmin;
jobs[c].zmax = zmax;
job_args[c] = &jobs[c];
}
sys_parallel_run(render_3d_draw_job, job_args, num_chunks);
}
}
static void render_3d_implicit(void) {
double step = range_3d * 2.0 / (GRID_3D - 1);
int z_steps = 100;
double zmin = 1e30, zmax = -1e30;
if (surface_needs_eval) {
int num_chunks = 4;
eval_job_t jobs[4];
void *job_args[4];
for (int j = 0; j < GRID_3D; j++) { for (int i = 0; i < GRID_3D; i++) { surf_v1[j][i] = false; surf_v2[j][i] = false; } }
int rows_per_chunk = GRID_3D / num_chunks;
for (int c = 0; c < num_chunks; c++) {
jobs[c].start_j = c * rows_per_chunk;
jobs[c].end_j = (c == num_chunks - 1) ? GRID_3D : (c + 1) * rows_per_chunk;
jobs[c].range = range_3d;
jobs[c].step = step;
job_args[c] = &jobs[c];
}
sys_parallel_run(eval_3d_implicit_job, job_args, num_chunks);
}
// Pass 2: Parallel Projection
{
int num_chunks = 4;
eval_job_t jobs[4];
void *job_args[4];
int rows_per_chunk = GRID_3D / num_chunks;
for (int c = 0; c < num_chunks; c++) {
jobs[c].start_j = c * rows_per_chunk;
jobs[c].end_j = (c == num_chunks - 1) ? GRID_3D : (c + 1) * rows_per_chunk;
jobs[c].range = range_3d;
jobs[c].step = step;
jobs[c].z_scale = 1.0;
job_args[c] = &jobs[c];
}
sys_parallel_run(eval_3d_project_job, job_args, num_chunks);
}
// Compute min/max for coloring based on what's visible
for (int j = 0; j < GRID_3D; j++) {
for (int i = 0; i < GRID_3D; i++) {
@ -1021,45 +1128,20 @@ static void render_3d_implicit(void) {
}
}
// Draw mesh for both surfaces
for (int j = 0; j < GRID_3D; j++) {
for (int i = 0; i < GRID_3D; i++) {
for (int s = 0; s < 2; s++) {
bool *v = (s == 0) ? surf_v1[j] : surf_v2[j];
double *z = (s == 0) ? surf_z1[j] : surf_z2[j];
if (!v[i]) continue;
int sx0, sy0;
project_3d(surf_x[j][i], z[i], surf_y_3d[j][i], &sx0, &sy0);
uint32_t col = color_by_height(z[i], zmin, zmax);
if (i+1 < GRID_3D && ((s == 0 && surf_v1[j][i+1]) || (s == 1 && surf_v2[j][i+1]))) {
int sx1, sy1;
double *znext = (s == 0) ? surf_z1[j] : surf_z2[j];
project_3d(surf_x[j][i+1], znext[i+1], surf_y_3d[j][i+1], &sx1, &sy1);
gfb_line(sx0, sy0, sx1, sy1, col);
}
if (j+1 < GRID_3D && ((s == 0 && surf_v1[j+1][i]) || (s == 1 && surf_v2[j+1][i]))) {
int sx1, sy1;
double *znext = (s == 0) ? surf_z1[j+1] : surf_z2[j+1];
project_3d(surf_x[j+1][i], znext[i], surf_y_3d[j+1][i], &sx1, &sy1);
gfb_line(sx0, sy0, sx1, sy1, col);
}
// Stitch the front and back roots together at the boundary to close zigzag gaps
if (s == 0 && surf_v1[j][i] && surf_v2[j][i]) {
bool edge = false;
if (i+1 < GRID_3D && !surf_v1[j][i+1]) edge = true;
if (i-1 >= 0 && !surf_v1[j][i-1]) edge = true;
if (j+1 < GRID_3D && !surf_v1[j+1][i]) edge = true;
if (j-1 >= 0 && !surf_v1[j-1][i]) edge = true;
if (edge) {
int sx2, sy2;
project_3d(surf_x[j][i], surf_z2[j][i], surf_y_3d[j][i], &sx2, &sy2);
gfb_line(sx0, sy0, sx2, sy2, col);
}
}
}
// Pass 3: Parallel Drawing
{
int num_chunks = 4;
draw_job_t jobs[4];
void *job_args[4];
int rows_per_chunk = GRID_3D / num_chunks;
for (int c = 0; c < num_chunks; c++) {
jobs[c].start_j = c * rows_per_chunk;
jobs[c].end_j = (c == num_chunks - 1) ? GRID_3D : (c + 1) * rows_per_chunk;
jobs[c].zmin = zmin;
jobs[c].zmax = zmax;
job_args[c] = &jobs[c];
}
sys_parallel_run(render_3d_draw_job, job_args, num_chunks);
}
}

View file

@ -920,7 +920,7 @@ static void explorer_paint(Window *win) {
DirtyRect dirty = graphics_get_dirty_rect();
graphics_set_clipping(offset_x, offset_y, win->w - 8, win->h - 28);
graphics_push_clipping(offset_x, offset_y, win->w - 8, win->h - 28);
draw_rect(offset_x, offset_y, win->w - 8, win->h - 28, COLOR_DARK_BG);
@ -984,7 +984,7 @@ static void explorer_paint(Window *win) {
int content_start_y = offset_y + 30;
graphics_set_clipping(win->x + 4, content_start_y, win->w - 8, win->h - 54 - 4);
graphics_push_clipping(win->x + 4, content_start_y, win->w - 8, win->h - 54 - 4);
for (int i = 0; i < state->item_count; i++) {
int row = i / EXPLORER_COLS;
@ -1010,11 +1010,8 @@ static void explorer_paint(Window *win) {
}
if (dirty.active) {
graphics_set_clipping(dirty.x, dirty.y, dirty.w, dirty.h);
} else {
graphics_clear_clipping();
}
graphics_pop_clipping(); // Pop content clipping
graphics_pop_clipping(); // Pop main window clipping
if (state->drive_menu_visible) {
int menu_x = win->x + 4;

View file

@ -31,11 +31,16 @@ static DirtyRect g_dirty = {0, 0, 0, 0, false};
#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;
#define MAX_RENDER_CPUS 32
#define CLIP_STACK_DEPTH 8
static int g_clip_stack_x[MAX_RENDER_CPUS][CLIP_STACK_DEPTH];
static int g_clip_stack_y[MAX_RENDER_CPUS][CLIP_STACK_DEPTH];
static int g_clip_stack_w[MAX_RENDER_CPUS][CLIP_STACK_DEPTH];
static int g_clip_stack_h[MAX_RENDER_CPUS][CLIP_STACK_DEPTH];
static int g_clip_stack_ptr[MAX_RENDER_CPUS] = {0};
static bool g_clip_enabled[MAX_RENDER_CPUS] = {false};
extern uint32_t smp_this_cpu_id(void);
#define MAX_RENDER_CPUS 32
static uint32_t *g_render_target[MAX_RENDER_CPUS] = {0};
static int g_rt_width[MAX_RENDER_CPUS] = {0};
static int g_rt_height[MAX_RENDER_CPUS] = {0};
@ -198,9 +203,10 @@ void put_pixel(int x, int y, uint32_t color) {
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) {
if (g_clip_enabled[cpu]) {
int ptr = g_clip_stack_ptr[cpu];
if (x < g_clip_stack_x[cpu][ptr] || x >= g_clip_stack_x[cpu][ptr] + g_clip_stack_w[cpu][ptr] ||
y < g_clip_stack_y[cpu][ptr] || y >= g_clip_stack_y[cpu][ptr] + g_clip_stack_h[cpu][ptr]) {
return;
}
}
@ -247,11 +253,12 @@ void draw_rect(int x, int y, int w, int h, uint32_t color) {
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 (g_clip_enabled[cpu]) {
int ptr = g_clip_stack_ptr[cpu];
if (x1 < g_clip_stack_x[cpu][ptr]) x1 = g_clip_stack_x[cpu][ptr];
if (y1 < g_clip_stack_y[cpu][ptr]) y1 = g_clip_stack_y[cpu][ptr];
if (x2 > g_clip_stack_x[cpu][ptr] + g_clip_stack_w[cpu][ptr]) x2 = g_clip_stack_x[cpu][ptr] + g_clip_stack_w[cpu][ptr];
if (y2 > g_clip_stack_y[cpu][ptr] + g_clip_stack_h[cpu][ptr]) y2 = g_clip_stack_y[cpu][ptr] + g_clip_stack_h[cpu][ptr];
}
if (x1 < 0) x1 = 0;
@ -409,16 +416,23 @@ void draw_rounded_rect_blurred(int x, int y, int w, int h, int radius, uint32_t
}
}
for (int c = 0; c < w; c++) {
for (int r = 0; r < h; r++) {
int g_y = y + r;
int g_x = x + c;
for (int r = 0; r < h; r++) {
int g_y = y + r;
if (g_y < 0 || g_y >= sh) continue;
if (g_clip_enabled) {
if (g_x < g_clip_x || g_x >= g_clip_x + g_clip_w ||
g_y < g_clip_y || g_y >= g_clip_y + g_clip_h) {
continue;
}
uint32_t cpu = smp_this_cpu_id();
if (g_clip_enabled[cpu]) {
int ptr = g_clip_stack_ptr[cpu];
if (g_y < g_clip_stack_y[cpu][ptr] || g_y >= g_clip_stack_y[cpu][ptr] + g_clip_stack_h[cpu][ptr]) continue;
}
for (int c = 0; c < w; c++) {
int g_x = x + c;
if (g_x < 0 || g_x >= sw) continue;
if (g_clip_enabled[cpu]) {
int ptr = g_clip_stack_ptr[cpu];
if (g_x < g_clip_stack_x[cpu][ptr] || g_x >= g_clip_stack_x[cpu][ptr] + g_clip_stack_w[cpu][ptr]) continue;
}
bool in_corner = false;
@ -478,9 +492,10 @@ void draw_char(int x, int y, char c, uint32_t color) {
uint32_t cpu = smp_this_cpu_id();
bool has_rt = (cpu < MAX_RENDER_CPUS && g_render_target[cpu]);
if (g_clip_enabled && !has_rt) {
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) {
if (g_clip_enabled[cpu] && !has_rt) {
int ptr = g_clip_stack_ptr[cpu];
if (x + 8 <= g_clip_stack_x[cpu][ptr] || x >= g_clip_stack_x[cpu][ptr] + g_clip_stack_w[cpu][ptr] ||
y + 8 <= g_clip_stack_y[cpu][ptr] || y >= g_clip_stack_y[cpu][ptr] + g_clip_stack_h[cpu][ptr]) {
return;
}
}
@ -502,9 +517,10 @@ void draw_char_bitmap(int x, int y, char c, uint32_t color) {
uint32_t cpu = smp_this_cpu_id();
bool has_rt = (cpu < MAX_RENDER_CPUS && g_render_target[cpu]);
if (g_clip_enabled && !has_rt) {
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) {
if (g_clip_enabled[cpu] && !has_rt) {
int ptr = g_clip_stack_ptr[cpu];
if (x + 8 <= g_clip_stack_x[cpu][ptr] || x >= g_clip_stack_x[cpu][ptr] + g_clip_stack_w[cpu][ptr] ||
y + 8 <= g_clip_stack_y[cpu][ptr] || y >= g_clip_stack_y[cpu][ptr] + g_clip_stack_h[cpu][ptr]) {
return;
}
}
@ -640,9 +656,11 @@ void draw_desktop_background(void) {
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;
uint32_t cpu = smp_this_cpu_id();
if (g_clip_enabled[cpu]) {
int ptr = g_clip_stack_ptr[cpu];
x1 = g_clip_stack_x[cpu][ptr]; y1 = g_clip_stack_y[cpu][ptr];
x2 = g_clip_stack_x[cpu][ptr] + g_clip_stack_w[cpu][ptr]; y2 = g_clip_stack_y[cpu][ptr] + g_clip_stack_h[cpu][ptr];
}
for (int y = y1; y < y2; y++) {
int src_y = y * g_bg_image_h / (int)g_fb->height;
@ -657,9 +675,11 @@ void draw_desktop_background(void) {
} 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;
uint32_t cpu = smp_this_cpu_id();
if (g_clip_enabled[cpu]) {
int ptr = g_clip_stack_ptr[cpu];
x1 = g_clip_stack_x[cpu][ptr]; y1 = g_clip_stack_y[cpu][ptr];
x2 = g_clip_stack_x[cpu][ptr] + g_clip_stack_w[cpu][ptr]; y2 = g_clip_stack_y[cpu][ptr] + g_clip_stack_h[cpu][ptr];
}
for (int y = y1; y < y2; y++) {
@ -889,33 +909,94 @@ void graphics_set_clipping(int x, int y, int w, int h) {
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;
uint32_t cpu = smp_this_cpu_id();
g_clip_stack_x[cpu][0] = x;
g_clip_stack_y[cpu][0] = y;
g_clip_stack_w[cpu][0] = w;
g_clip_stack_h[cpu][0] = h;
g_clip_stack_ptr[cpu] = 0; // Reset to base
g_clip_enabled[cpu] = true;
}
void graphics_push_clipping(int x, int y, int w, int h) {
uint32_t cpu = smp_this_cpu_id();
int cur_ptr = g_clip_stack_ptr[cpu];
if (cur_ptr + 1 >= CLIP_STACK_DEPTH) return; // Stack overflow
// Intersect with current top
int cx1 = g_clip_stack_x[cpu][cur_ptr];
int cy1 = g_clip_stack_y[cpu][cur_ptr];
int cx2 = cx1 + g_clip_stack_w[cpu][cur_ptr];
int cy2 = cy1 + g_clip_stack_h[cpu][cur_ptr];
int nx1 = x;
int ny1 = y;
int nx2 = x + w;
int ny2 = y + h;
if (nx1 < cx1) nx1 = cx1;
if (ny1 < cy1) ny1 = cy1;
if (nx2 > cx2) nx2 = cx2;
if (ny2 > cy2) ny2 = cy2;
int nw = nx2 - nx1;
int nh = ny2 - ny1;
if (nw < 0) nw = 0;
if (nh < 0) nh = 0;
g_clip_stack_ptr[cpu]++;
g_clip_stack_x[cpu][cur_ptr + 1] = nx1;
g_clip_stack_y[cpu][cur_ptr + 1] = ny1;
g_clip_stack_w[cpu][cur_ptr + 1] = nw;
g_clip_stack_h[cpu][cur_ptr + 1] = nh;
g_clip_enabled[cpu] = true;
}
void graphics_pop_clipping(void) {
uint32_t cpu = smp_this_cpu_id();
if (g_clip_stack_ptr[cpu] > 0) {
g_clip_stack_ptr[cpu]--;
} else {
g_clip_enabled[cpu] = false;
}
}
void graphics_clear_clipping(void) {
g_clip_enabled = false;
uint32_t cpu = smp_this_cpu_id();
g_clip_stack_ptr[cpu] = 0;
g_clip_enabled[cpu] = 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;
uint32_t cpu = smp_this_cpu_id();
int cx1 = 0, cy1 = 0, cx2 = sw, cy2 = sh;
if (g_clip_enabled[cpu]) {
int ptr = g_clip_stack_ptr[cpu];
cx1 = g_clip_stack_x[cpu][ptr];
cy1 = g_clip_stack_y[cpu][ptr];
cx2 = cx1 + g_clip_stack_w[cpu][ptr];
cy2 = cy1 + g_clip_stack_h[cpu][ptr];
}
for (int x = 0; x < w; x++) {
int vx = dst_x + x;
if (vx < 0 || vx >= sw) continue;
int x1 = dst_x, y1 = dst_y, x2 = dst_x + w, y2 = dst_y + h;
if (x1 < cx1) x1 = cx1;
if (y1 < cy1) y1 = cy1;
if (x2 > cx2) x2 = cx2;
if (y2 > cy2) y2 = cy2;
uint32_t pcol = src[y * w + x];
if (x1 >= x2 || y1 >= y2) return;
for (int y = y1; y < y2; y++) {
uint32_t *dst_row = &g_back_buffer[y * sw + x1];
uint32_t *src_row = &src[(y - dst_y) * w + (x1 - dst_x)];
int len = x2 - x1;
for (int x = 0; x < len; x++) {
uint32_t pcol = src_row[x];
if ((pcol & 0xFF000000) != 0 || (pcol & 0xFFFFFF) != 0) {
g_back_buffer[vy * sw + vx] = pcol;
dst_row[x] = pcol;
}
}
}

View file

@ -57,6 +57,8 @@ void graphics_clear_back_buffer(uint32_t color);
// Clipping
void graphics_set_clipping(int x, int y, int w, int h);
void graphics_push_clipping(int x, int y, int w, int h);
void graphics_pop_clipping(void);
void graphics_clear_clipping(void);
// Font access (requires font_manager.h for ttf_font_t)

View file

@ -17,6 +17,8 @@
#include "userland/stb_image.h"
#include "memory_manager.h"
#include "disk.h"
#include "../sys/work_queue.h"
#include "../sys/smp.h"
// Hello developer,
@ -98,6 +100,14 @@ static int drag_offset_y = 0;
bool is_dragging_file = false;
static char drag_file_path[FAT32_MAX_PATH];
static int drag_icon_type = 0;
typedef struct {
int y_start;
int y_end;
DirtyRect dirty;
volatile int *completion_counter;
int pass;
} wm_strip_job_t;
static int drag_start_x = 0;
static int drag_start_y = 0;
static int drag_icon_orig_x = 0;
@ -111,10 +121,8 @@ static int window_count = 0;
// Redraw system
static bool force_redraw = true;
static uint32_t timer_ticks = 0;
static int desktop_refresh_timer = 0;
// Cursor state
static bool cursor_visible = true;
static int last_cursor_x = 400;
static int last_cursor_y = 300;
@ -1110,27 +1118,6 @@ static void draw_dock_clock(int x, int y) {
draw_rect(cx, cy - 1, 10, 2, 0xFF333333);
}
static void draw_dock_editor(int x, int y) {
draw_rounded_rect_filled(x, y, 48, 48, 10, 0xFF0A1628);
draw_rounded_rect_filled(x + 1, y + 1, 46, 28, 9, 0xFF1565C0);
draw_rounded_rect_filled(x + 1, y + 24, 46, 23, 9, 0xFF0D47A1);
draw_rect(x + 5, y + 8, 9, 32, 0xFF1A237E);
draw_filled_circle(x + 10, y + 14, 2, 0xFF7986CB);
draw_filled_circle(x + 10, y + 22, 2, 0xFF7986CB);
draw_filled_circle(x + 10, y + 30, 2, 0xFF7986CB);
draw_rect(x + 15, y + 8, 28, 32, 0xFF1B2B3C);
draw_rect(x + 15, y + 8, 14, 5, 0xFF1B2B3C);
draw_rect(x + 15, y + 8, 14, 1, 0xFF569CD6);
draw_rect(x + 18, y + 13, 9, 2, 0xFF569CD6);
draw_rect(x + 29, y + 13, 8, 2, 0xFF4EC9B0);
draw_rect(x + 18, y + 18, 5, 2, 0xFFCE9178);
draw_rect(x + 25, y + 18, 7, 2, 0xFFCE9178);
draw_rect(x + 21, y + 23, 7, 2, 0xFF9CDCFE);
draw_rect(x + 30, y + 23, 5, 2, 0xFFD4D4D4);
draw_rect(x + 18, y + 28, 16, 2, 0xFF6A9955);
draw_rect(x + 18, y + 33, 10, 2, 0xFFD4D4D4);
draw_rect(x + 30, y + 33, 6, 2, 0xFF569CD6);
}
void draw_window(Window *win) {
if (!win->visible) return;
@ -1240,6 +1227,7 @@ static void erase_cursor(int x, int y) {
}
// --- Clock ---
static uint8_t rtc_read(uint8_t reg) {
outb(0x70, reg);
return inb(0x71);
@ -1274,275 +1262,263 @@ static void draw_clock(int x, int y) {
}
// --- Main Paint Function ---
void wm_paint(void) {
bool rect_contains(int x, int y, int w, int h, int px, int py) {
return px >= x && px < x + w && py >= y && py < y + h;
}
static Window *sorted_windows_cache[32];
static int sorted_window_count_cache = 0;
static void wm_paint_region(int y_start, int y_end, DirtyRect dirty, int pass) {
int sw = get_screen_width();
int sh = get_screen_height();
int cx = 0, cy = y_start, cw = sw, ch = y_end - y_start;
if (dirty.active) {
if (cx < dirty.x) { cw -= (dirty.x - cx); cx = dirty.x; }
if (cy < dirty.y) { ch -= (dirty.y - cy); cy = dirty.y; }
if (cx + cw > dirty.x + dirty.w) cw = dirty.x + dirty.w - cx;
if (cy + ch > dirty.y + dirty.h) ch = dirty.y + dirty.h - cy;
}
if (cw <= 0 || ch <= 0) return;
graphics_set_clipping(cx, cy, cw, ch);
if (pass == 1) {
draw_desktop_background();
for (int i = 0; i < desktop_icon_count; i++) {
DesktopIcon *icon = &desktop_icons[i];
if (icon->y + 85 <= cy || icon->y >= cy + ch) continue;
if (dirty.active && (icon->x + 85 <= dirty.x || icon->x >= dirty.x + dirty.w)) continue;
if (icon->type == 1) draw_folder_icon(icon->x, icon->y, icon->name);
else if (icon->type == 2) {
char label[64]; int len = 0;
while(icon->name[len] && len < 63) { label[len] = icon->name[len]; len++; }
label[len] = 0;
if (len > 9 && str_ends_with(label, ".shortcut")) label[len-9] = 0;
if (str_starts_with(icon->name, "Notepad")) draw_notepad_icon(icon->x, icon->y, label);
else if (str_starts_with(icon->name, "Calculator")) draw_calculator_icon(icon->x, icon->y, label);
else if (str_starts_with(icon->name, "Terminal")) draw_terminal_icon(icon->x, icon->y, label);
else if (str_starts_with(icon->name, "Minesweeper")) draw_minesweeper_icon(icon->x, icon->y, label);
else if (str_starts_with(icon->name, "Settings")) draw_control_panel_icon(icon->x, icon->y, label);
else if (str_starts_with(icon->name, "Clock")) draw_clock_icon(icon->x, icon->y, label);
else if (str_starts_with(icon->name, "About")) draw_about_icon(icon->x, icon->y, label);
else if (str_starts_with(icon->name, "Recycle Bin")) draw_recycle_bin_icon(icon->x, icon->y, label);
else if (str_starts_with(icon->name, "Files")) draw_folder_icon(icon->x, icon->y, label);
else if (str_starts_with(icon->name, "Paint")) draw_paint_icon(icon->x, icon->y, label);
else draw_icon(icon->x, icon->y, label);
} else {
if (str_ends_with(icon->name, ".elf")) draw_elf_icon(icon->x, icon->y, icon->name);
else if (str_ends_with(icon->name, ".pnt")) draw_paint_icon(icon->x, icon->y, icon->name);
else if (is_image_file(icon->name)) {
char full_path[128] = "/Desktop/"; int p=9; int n=0; while(icon->name[n] && p < 127) full_path[p++] = icon->name[n++]; full_path[p]=0;
draw_image_icon(icon->x, icon->y, full_path);
draw_icon_label(icon->x, icon->y, icon->name);
}
else if (str_ends_with(icon->name, ".pdf")) draw_pdf_icon(icon->x, icon->y, icon->name);
else draw_document_icon(icon->x, icon->y, icon->name);
}
}
for (int i = 0; i < sorted_window_count_cache; i++) {
Window *win = sorted_windows_cache[i];
if (!win || !win->visible) continue;
if (win->y + win->h <= cy || win->y >= cy + ch) continue;
if (dirty.active && !win->focused && (win->x + win->w <= dirty.x || win->x >= dirty.x + dirty.w)) continue;
draw_window(win);
}
} else if (pass == 2) {
if (0 < cy + ch && 30 > cy) {
draw_rect(0, 0, sw, 30, COLOR_TOPBAR_BG);
draw_boredos_logo(8, 8, 1);
draw_clock(sw - 80, 12);
}
if (start_menu_open && 40 < cy + ch && 125 > cy) {
draw_rounded_rect_filled(8, 40, 160, 85, 8, COLOR_DARK_PANEL);
draw_string(20, 48, "About BoredOS", COLOR_DARK_TEXT);
draw_string(20, 68, "Settings", COLOR_DARK_TEXT);
draw_string(20, 88, "Shutdown", COLOR_DARK_TEXT);
draw_string(20, 108, "Restart", COLOR_DARK_TEXT);
}
int dock_h = 60, dock_y = sh - dock_h - 6;
if (dock_y < cy + ch && dock_y + dock_h > cy) {
int d_item_sz = 48, d_space = 10, d_total_w = 11 * (d_item_sz + d_space);
int d_bg_x = (sw - d_total_w) / 2 - 12, d_bg_w = d_total_w + 24;
draw_rounded_rect_blurred(d_bg_x, dock_y, d_bg_w, dock_h, 18, COLOR_DOCK_BG, 5, 140);
int dx = (sw - d_total_w) / 2, dy = dock_y + 6;
draw_dock_files(dx, dy); dx += d_item_sz+d_space;
draw_dock_settings(dx, dy); dx += d_item_sz+d_space;
draw_dock_notepad(dx, dy); dx += d_item_sz+d_space;
draw_dock_calculator(dx, dy); dx += d_item_sz+d_space;
draw_dock_terminal(dx, dy); dx += d_item_sz+d_space;
draw_dock_minesweeper(dx, dy); dx += d_item_sz+d_space;
draw_dock_paint(dx, dy); dx += d_item_sz+d_space;
draw_dock_browser(dx, dy); dx += d_item_sz+d_space;
draw_dock_taskman(dx, dy); dx += d_item_sz+d_space;
draw_dock_clock(dx, dy); dx += d_item_sz+d_space;
draw_dock_word(dx, dy);
}
if (desktop_menu_visible) {
int d_mw = 140, d_mh = (desktop_menu_target_icon != -1) ? 125 : 75;
if (desktop_menu_y < cy + ch && desktop_menu_y + d_mh > cy) {
draw_rounded_rect_filled(desktop_menu_x, desktop_menu_y, d_mw, d_mh, 8, COLOR_DARK_PANEL);
int item_h = 25;
if (desktop_menu_target_icon != -1) {
bool cp = explorer_clipboard_has_content();
draw_string(desktop_menu_x + 10, desktop_menu_y + 5, "Cut", COLOR_WHITE);
draw_string(desktop_menu_x + 10, desktop_menu_y + 5 + item_h, "Copy", COLOR_WHITE);
draw_string(desktop_menu_x + 10, desktop_menu_y + 5 + item_h * 2, "Paste", cp ? COLOR_WHITE : COLOR_DKGRAY);
draw_string(desktop_menu_x + 10, desktop_menu_y + 5 + item_h * 3, "Delete", COLOR_TRAFFIC_RED);
draw_string(desktop_menu_x + 10, desktop_menu_y + 5 + item_h * 4, "Rename", COLOR_WHITE);
} else {
bool cp = explorer_clipboard_has_content();
draw_string(desktop_menu_x + 10, desktop_menu_y + 5, "New File", COLOR_WHITE);
draw_string(desktop_menu_x + 10, desktop_menu_y + 5 + item_h, "New Folder", COLOR_WHITE);
draw_string(desktop_menu_x + 10, desktop_menu_y + 5 + item_h * 2, "Paste", cp ? COLOR_WHITE : COLOR_DKGRAY);
}
}
}
if (desktop_dialog_state != 0) {
int dlg_w = 300, dlg_h = 110, dg_x = (sw - dlg_w)/2, dg_y = (sh - dlg_h)/2;
if (dg_y < cy + ch && dg_y + dlg_h > cy) {
draw_rounded_rect_filled(dg_x, dg_y, dlg_w, dlg_h, 8, COLOR_DARK_PANEL);
const char *title = (desktop_dialog_state == 1) ? "Create New File" : (desktop_dialog_state == 2 ? "Create New Folder" : "Rename");
const char *btn = (desktop_dialog_state == 0) ? "Rename" : "Create";
draw_string(dg_x + 10, dg_y + 10, title, COLOR_WHITE);
draw_rounded_rect_filled(dg_x + 10, dg_y + 35, 280, 20, 4, COLOR_DARK_BG);
draw_string(dg_x + 15, dg_y + 40, desktop_dialog_input, COLOR_WHITE);
char temp_sub[64]; int k; for (k=0; k<desktop_dialog_cursor&&desktop_dialog_input[k]; k++) temp_sub[k]=desktop_dialog_input[k]; temp_sub[k]=0;
draw_rect(dg_x + 15 + font_manager_get_string_width(graphics_get_current_ttf(), temp_sub), dg_y + 39, 2, 12, COLOR_WHITE);
draw_rounded_rect_filled(dg_x + 50, dg_y + 65, 80, 25, 4, COLOR_DARK_BORDER); draw_string(dg_x + 70, dg_y + 72, btn, COLOR_WHITE);
draw_rounded_rect_filled(dg_x + 170, dg_y + 65, 80, 25, 4, COLOR_DARK_BORDER); draw_string(dg_x + 185, dg_y + 72, "Cancel", COLOR_WHITE);
}
}
if (msg_box_visible) {
int mw = 320, mh = 100, m_x = (sw - mw)/2, m_y = (sh - mh)/2;
if (m_y < cy + ch && m_y + mh > cy) {
draw_rounded_rect_filled(m_x, m_y, mw, mh, 8, COLOR_DARK_PANEL);
draw_string(m_x + 15, m_y + 10, msg_box_title, COLOR_DARK_TEXT);
draw_string(m_x + 10, m_y + 40, msg_box_text, COLOR_DARK_TEXT);
draw_rounded_rect_filled(m_x + mw/2 - 30, m_y + 70, 60, 20, 4, COLOR_DARK_BORDER);
draw_string(m_x + mw/2 - 10, m_y + 75, "OK", COLOR_WHITE);
}
}
if (notif_active) {
int nx = sw - 400 + notif_x_offset, ny = 40, nw = 380, nh = 50;
if (ny < cy + ch && ny + nh > cy) {
draw_rounded_rect_filled(nx, ny, nw, nh, 8, COLOR_DARK_PANEL);
draw_string(nx + 15, ny + 10, "Screenshot", COLOR_DARK_TEXT);
draw_string(nx + 15, ny + 30, notif_text, COLOR_DKGRAY);
}
}
if (wm_custom_paint_hook) wm_custom_paint_hook();
if (is_dragging_file) {
if (mx - 20 < cx + cw && mx + 20 > cx && my - 20 < cy + ch && my + 20 > cy) {
if (drag_icon_type == 1) draw_folder_icon(mx - 20, my - 20, "Moving...");
else if (drag_icon_type == 2) draw_icon(mx - 20, my - 20, "Moving...");
else draw_document_icon(mx - 20, my - 20, "Moving...");
}
}
}
}
static void wm_strip_worker_job(void *arg) {
wm_strip_job_t *job = (wm_strip_job_t *)arg;
wm_paint_region(job->y_start, job->y_end, job->dirty, job->pass);
__atomic_sub_fetch(job->completion_counter, 1, __ATOMIC_SEQ_CST);
}
void wm_paint(void) {
int sw = get_screen_width();
int sh = get_screen_height();
uint64_t rflags;
rflags = wm_lock_acquire();
wm_mark_dirty(last_cursor_x, last_cursor_y, 12, 12);
wm_mark_dirty(mx, my, 12, 12);
DirtyRect dirty = graphics_get_dirty_rect();
if (dirty.active) {
int d_h = 60;
int d_y = sh - d_h - 6;
int d_item_sz = 48;
int d_space = 10;
int d_tw = 10 * (d_item_sz + d_space);
int d_bg_x = (sw - d_tw) / 2 - 12;
int d_bg_w = d_tw + 24;
int d_h = 60, d_y = sh - d_h - 6, d_total_w = 11 * (48 + 10);
int d_bg_x = (sw - d_total_w) / 2 - 12, d_bg_w = d_total_w + 24;
if (!(dirty.x >= d_bg_x + d_bg_w || dirty.x + dirty.w <= d_bg_x ||
dirty.y >= d_y + d_h || dirty.y + dirty.h <= d_y)) {
graphics_mark_dirty(d_bg_x - 10, d_y - 10, d_bg_w + 20, d_h + 20);
dirty = graphics_get_dirty_rect();
}
graphics_set_clipping(dirty.x, dirty.y, dirty.w, dirty.h);
} else {
graphics_clear_clipping();
}
// 1. Desktop Background (respects wallpaper color/pattern)
draw_desktop_background();
// Draw Desktop Icons
for (int i = 0; i < desktop_icon_count; i++) {
DesktopIcon *icon = &desktop_icons[i];
if (dirty.active) {
if (icon->x + 80 <= dirty.x || icon->x >= dirty.x + dirty.w ||
icon->y + 80 <= dirty.y || icon->y >= dirty.y + dirty.h) {
continue;
}
}
if (icon->type == 1) draw_folder_icon(icon->x, icon->y, icon->name);
else if (icon->type == 2) {
// App icon - strip .shortcut for display
char label[64];
int len = 0;
while(icon->name[len] && len < 63) { label[len] = icon->name[len]; len++; }
label[len] = 0;
if (len > 9 && str_ends_with(label, ".shortcut")) {
label[len-9] = 0;
}
if (str_starts_with(icon->name, "Notepad")) draw_notepad_icon(icon->x, icon->y, label);
else if (str_starts_with(icon->name, "Calculator")) draw_calculator_icon(icon->x, icon->y, label);
else if (str_starts_with(icon->name, "Terminal")) draw_terminal_icon(icon->x, icon->y, label);
else if (str_starts_with(icon->name, "Minesweeper")) draw_minesweeper_icon(icon->x, icon->y, label);
else if (str_starts_with(icon->name, "Settings")) draw_control_panel_icon(icon->x, icon->y, label);
else if (str_starts_with(icon->name, "Clock")) draw_clock_icon(icon->x, icon->y, label);
else if (str_starts_with(icon->name, "About")) draw_about_icon(icon->x, icon->y, label);
else if (str_starts_with(icon->name, "Recycle Bin")) draw_recycle_bin_icon(icon->x, icon->y, label);
else if (str_starts_with(icon->name, "Files")) draw_folder_icon(icon->x, icon->y, label);
else if (str_starts_with(icon->name, "Paint")) draw_paint_icon(icon->x, icon->y, label);
else draw_icon(icon->x, icon->y, label);
} else {
if (str_ends_with(icon->name, ".elf")) draw_elf_icon(icon->x, icon->y, icon->name);
else if (str_ends_with(icon->name, ".pnt")) draw_paint_icon(icon->x, icon->y, icon->name);
else if (is_image_file(icon->name)) {
char full_path[128] = "/Desktop/";
int p=9; int n=0; while(icon->name[n] && p < 127) full_path[p++] = icon->name[n++]; full_path[p]=0;
draw_image_icon(icon->x, icon->y, full_path);
draw_icon_label(icon->x, icon->y, icon->name);
}
else if (str_ends_with(icon->name, ".pdf")) draw_pdf_icon(icon->x, icon->y, icon->name);
else draw_document_icon(icon->x, icon->y, icon->name);
}
}
// 3. Windows - sort by z-index and draw
int local_window_count = window_count;
Window *sorted_windows[32];
for (int i = 0; i < local_window_count; i++) {
sorted_windows[i] = all_windows[i];
}
for (int i = 0; i < local_window_count - 1; i++) {
for (int j = 0; j < local_window_count - i - 1; j++) {
if (sorted_windows[j] && sorted_windows[j + 1] &&
sorted_windows[j]->z_index > sorted_windows[j + 1]->z_index) {
Window *temp = sorted_windows[j];
sorted_windows[j] = sorted_windows[j + 1];
sorted_windows[j + 1] = temp;
sorted_window_count_cache = window_count;
if (sorted_window_count_cache > 32) sorted_window_count_cache = 32;
for (int i = 0; i < sorted_window_count_cache; i++) sorted_windows_cache[i] = all_windows[i];
for (int i = 0; i < sorted_window_count_cache - 1; i++) {
for (int j = 0; j < sorted_window_count_cache - i - 1; j++) {
if (sorted_windows_cache[j] && sorted_windows_cache[j+1] &&
sorted_windows_cache[j]->z_index > sorted_windows_cache[j+1]->z_index) {
Window *tmp = sorted_windows_cache[j];
sorted_windows_cache[j] = sorted_windows_cache[j+1];
sorted_windows_cache[j+1] = tmp;
}
}
}
for (int i = 0; i < local_window_count; i++) {
Window *win = sorted_windows[i];
if (!win || !win->visible) continue;
// Memory barrier to ensure APs see the sorted window list correctly
asm volatile("" ::: "memory");
if (dirty.active && !win->focused) {
if (win->x + win->w <= dirty.x || win->x >= dirty.x + dirty.w ||
win->y + win->h <= dirty.y || win->y >= dirty.y + dirty.h) {
continue;
}
}
draw_window(win);
uint32_t cpu_count = smp_cpu_count();
if (cpu_count > 32) cpu_count = 32;
if (cpu_count < 1) cpu_count = 1;
volatile int completion_counter = (int)cpu_count;
wm_strip_job_t jobs[32];
int rows_per_strip = sh / cpu_count;
// PASS 1: BACKGROUND & WINDOWS
for (uint32_t i = 0; i < cpu_count; i++) {
jobs[i].y_start = i * rows_per_strip;
jobs[i].y_end = (i == cpu_count - 1) ? sh : (i + 1) * rows_per_strip;
jobs[i].dirty = dirty;
jobs[i].completion_counter = &completion_counter;
jobs[i].pass = 1;
if (i < cpu_count - 1) work_queue_submit(wm_strip_worker_job, &jobs[i]);
}
wm_paint_region(jobs[cpu_count-1].y_start, jobs[cpu_count-1].y_end, dirty, 1);
__atomic_sub_fetch(&completion_counter, 1, __ATOMIC_SEQ_CST);
while (completion_counter > 0) {
if (!work_queue_drain_one()) asm volatile("pause");
}
draw_rect(0, 0, sw, 30, COLOR_TOPBAR_BG);
draw_boredos_logo(8, 8, 1);
draw_clock(sw - 80, 12);
if (start_menu_open) {
int menu_h = 85;
draw_rounded_rect_filled(8, 40, 160, menu_h, 8, COLOR_DARK_PANEL);
draw_string(20, 48, "About BoredOS", COLOR_DARK_TEXT);
draw_string(20, 68, "Settings", COLOR_DARK_TEXT);
draw_string(20, 88, "Shutdown", COLOR_DARK_TEXT);
draw_string(20, 108, "Restart", COLOR_DARK_TEXT);
// PASS 2: UI OVERLAY (Dock, start menu, menus etc)
completion_counter = (int)cpu_count;
for (uint32_t i = 0; i < cpu_count; i++) {
jobs[i].pass = 2;
if (i < cpu_count - 1) work_queue_submit(wm_strip_worker_job, &jobs[i]);
}
wm_paint_region(jobs[cpu_count-1].y_start, jobs[cpu_count-1].y_end, dirty, 2);
__atomic_sub_fetch(&completion_counter, 1, __ATOMIC_SEQ_CST);
while (completion_counter > 0) {
if (!work_queue_drain_one()) asm volatile("pause");
}
int dock_h = 60;
int dock_y = sh - dock_h - 6;
int dock_item_size = 48;
int dock_spacing = 10;
int total_dock_width = 11 * (dock_item_size + dock_spacing);
int dock_bg_x = (sw - total_dock_width) / 2 - 12;
int dock_bg_w = total_dock_width + 24;
// Draw blurred dock background with reduced radius and tint
draw_rounded_rect_blurred(dock_bg_x, dock_y, dock_bg_w, dock_h, 18, COLOR_DOCK_BG, 5, 140);
int dock_x = (sw - total_dock_width) / 2;
int dock_item_y = dock_y + 6;
draw_dock_files(dock_x, dock_item_y);
dock_x += dock_item_size + dock_spacing;
draw_dock_settings(dock_x, dock_item_y);
dock_x += dock_item_size + dock_spacing;
draw_dock_notepad(dock_x, dock_item_y);
dock_x += dock_item_size + dock_spacing;
draw_dock_calculator(dock_x, dock_item_y);
dock_x += dock_item_size + dock_spacing;
draw_dock_terminal(dock_x, dock_item_y);
dock_x += dock_item_size + dock_spacing;
draw_dock_minesweeper(dock_x, dock_item_y);
dock_x += dock_item_size + dock_spacing;
draw_dock_paint(dock_x, dock_item_y);
dock_x += dock_item_size + dock_spacing;
draw_dock_browser(dock_x, dock_item_y);
dock_x += dock_item_size + dock_spacing;
draw_dock_taskman(dock_x, dock_item_y);
dock_x += dock_item_size + dock_spacing;
draw_dock_clock(dock_x, dock_item_y);
dock_x += dock_item_size + dock_spacing;
draw_dock_word(dock_x, dock_item_y);
// Desktop Context Menu (with rounded corners)
if (desktop_menu_visible) {
int menu_w = 140;
int item_h = 25;
int menu_h = (desktop_menu_target_icon != -1) ? 125 : 75;
draw_rounded_rect_filled(desktop_menu_x, desktop_menu_y, menu_w, menu_h, 8, COLOR_DARK_PANEL);
if (desktop_menu_target_icon != -1) {
bool can_paste = explorer_clipboard_has_content();
draw_string(desktop_menu_x + 10, desktop_menu_y + 5, "Cut", COLOR_WHITE);
draw_string(desktop_menu_x + 10, desktop_menu_y + 5 + item_h, "Copy", COLOR_WHITE);
draw_string(desktop_menu_x + 10, desktop_menu_y + 5 + item_h * 2, "Paste", can_paste ? COLOR_WHITE : COLOR_DKGRAY);
draw_string(desktop_menu_x + 10, desktop_menu_y + 5 + item_h * 3, "Delete", COLOR_TRAFFIC_RED);
draw_string(desktop_menu_x + 10, desktop_menu_y + 5 + item_h * 4, "Rename", COLOR_WHITE);
} else {
bool can_paste = explorer_clipboard_has_content();
draw_string(desktop_menu_x + 10, desktop_menu_y + 5, "New File", COLOR_WHITE);
draw_string(desktop_menu_x + 10, desktop_menu_y + 5 + item_h, "New Folder", COLOR_WHITE);
draw_string(desktop_menu_x + 10, desktop_menu_y + 5 + item_h * 2, "Paste", can_paste ? COLOR_WHITE : COLOR_DKGRAY);
}
}
// Desktop Dialogs (dark mode)
if (desktop_dialog_state != 0) {
int dlg_w = 300; int dlg_h = 110;
int dlg_x = (sw - dlg_w) / 2;
int dlg_y = (sh - dlg_h) / 2;
draw_rounded_rect_filled(dlg_x, dlg_y, dlg_w, dlg_h, 8, COLOR_DARK_PANEL);
const char *title = "Rename";
const char *btn_text = "Rename";
if (desktop_dialog_state == 1) { title = "Create New File"; btn_text = "Create"; }
else if (desktop_dialog_state == 2) { title = "Create New Folder"; btn_text = "Create"; }
draw_string(dlg_x + 10, dlg_y + 10, title, COLOR_WHITE);
draw_rounded_rect_filled(dlg_x + 10, dlg_y + 35, 280, 20, 4, COLOR_DARK_BG);
draw_string(dlg_x + 15, dlg_y + 40, desktop_dialog_input, COLOR_WHITE);
// Cursor
char sub[64];
int k;
for (k = 0; k < desktop_dialog_cursor && desktop_dialog_input[k]; k++) sub[k] = desktop_dialog_input[k];
sub[k] = 0;
int cx = font_manager_get_string_width(graphics_get_current_ttf(), sub);
draw_rect(dlg_x + 15 + cx, dlg_y + 39, 2, 12, COLOR_WHITE);
draw_rounded_rect_filled(dlg_x + 50, dlg_y + 65, 80, 25, 4, COLOR_DARK_BORDER);
draw_string(dlg_x + 70, dlg_y + 72, btn_text, COLOR_WHITE);
draw_rounded_rect_filled(dlg_x + 170, dlg_y + 65, 80, 25, 4, COLOR_DARK_BORDER);
draw_string(dlg_x + 185, dlg_y + 72, "Cancel", COLOR_WHITE);
}
// Message Box (dark mode)
if (msg_box_visible) {
int mw = 320;
int mh = 100;
int mx = (sw - mw) / 2;
int my = (sh - mh) / 2;
draw_rounded_rect_filled(mx, my, mw, mh, 8, COLOR_DARK_PANEL);
draw_string(mx + 15, my + 10, msg_box_title, COLOR_DARK_TEXT);
draw_string(mx + 10, my + 40, msg_box_text, COLOR_DARK_TEXT);
draw_rounded_rect_filled(mx + mw/2 - 30, my + 70, 60, 20, 4, COLOR_DARK_BORDER);
}
// Notification (dark mode)
if (notif_active) {
int nx = sw - 400 + notif_x_offset;
int ny = 40;
int nw = 380;
int nh = 50;
draw_rounded_rect_filled(nx, ny, nw, nh, 8, COLOR_DARK_PANEL);
draw_string(nx + 15, ny + 10, "Screenshot", COLOR_DARK_TEXT);
draw_string(nx + 15, ny + 30, notif_text, COLOR_DKGRAY);
}
// Custom Overlay (VM Graphics)
if (wm_custom_paint_hook) {
wm_custom_paint_hook();
}
// Draw Dragged Icon
if (is_dragging_file) {
if (drag_icon_type == 1) draw_folder_icon(mx - 20, my - 20, "Moving...");
else if (drag_icon_type == 2) draw_icon(mx - 20, my - 20, "Moving...");
else draw_document_icon(mx - 20, my - 20, "Moving...");
}
// 7. Mouse cursor (draw last so it's on top)
graphics_clear_clipping();
draw_cursor(mx, my);
last_cursor_x = mx;
last_cursor_y = my;
// Flip the buffer - display the rendered frame atomically
graphics_flip_buffer();
graphics_clear_dirty_no_lock();
// Restore IRQs
wm_lock_release(rflags);
}
// --- Input Handling ---
bool rect_contains(int x, int y, int w, int h, int px, int py) {
return px >= x && px < x + w && py >= y && py < y + h;
}
void wm_bring_to_front_locked(Window *win) {
for (int i = 0; i < window_count; i++) {
all_windows[i]->focused = false;
@ -1725,7 +1701,8 @@ void wm_handle_click(int x, int y) {
if (desktop_snap_to_grid) {
int col = (desktop_icons[new_idx].x - 20 + 40) / 80;
int row = (desktop_icons[new_idx].y - 20 + 40) / 80;
if (col < 0) col = 0; if (row < 0) row = 0;
if (col < 0) col = 0;
if (row < 0) row = 0;
desktop_icons[new_idx].x = 20 + col * 80;
desktop_icons[new_idx].y = 20 + row * 80;
}
@ -2414,7 +2391,8 @@ static void wm_handle_mouse_internal(int dx, int dy, uint8_t buttons, int dz) {
if (desktop_snap_to_grid) {
int col = (desktop_icons[i].x - 20 + 40) / 80;
int row = (desktop_icons[i].y - 20 + 40) / 80;
if (col < 0) col = 0; if (row < 0) row = 0;
if (col < 0) col = 0;
if (row < 0) row = 0;
desktop_icons[i].x = 20 + col * 80;
desktop_icons[i].y = 20 + row * 80;
}