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 "platform.h"
#include "paging.h" #include "paging.h"
#include "process.h" #include "process.h"
#include "work_queue.h"
extern void serial_write(const char *str); extern void serial_write(const char *str);
extern void serial_write_num(uint32_t n); extern void serial_write_num(uint32_t n);
extern void serial_write_hex(uint64_t n); extern void serial_write_hex(uint64_t n);
// --- Dynamically allocated per-CPU state --- static cpu_state_t *cpu_states = NULL;
static cpu_state_t *cpu_states = NULL; // Array[cpu_count]
static uint32_t total_cpus = 0; static uint32_t total_cpus = 0;
static uint32_t bsp_lapic_id = 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) { static uint32_t read_lapic_id(void) {
uint32_t eax, ebx, ecx, edx; uint32_t eax, ebx, ecx, edx;
asm volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(1)); 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]; 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) { 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); 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; uint64_t cr0;
asm volatile("mov %%cr0, %0" : "=r"(cr0)); asm volatile("mov %%cr0, %0" : "=r"(cr0));
cr0 &= ~(1ULL << 2); // Clear EM cr0 &= ~(1ULL << 2);
cr0 |= (1ULL << 1); // Set MP cr0 |= (1ULL << 1);
cr0 |= (1ULL << 5); // Set NE cr0 |= (1ULL << 5);
asm volatile("mov %0, %%cr0" : : "r"(cr0)); asm volatile("mov %0, %%cr0" : : "r"(cr0));
uint64_t cr4; uint64_t cr4;
asm volatile("mov %%cr4, %0" : "=r"(cr4)); asm volatile("mov %%cr4, %0" : "=r"(cr4));
cr4 |= (1ULL << 9); // OSFXSR cr4 |= (1ULL << 9);
cr4 |= (1ULL << 10); // OSXMMEXCPT cr4 |= (1ULL << 10);
asm volatile("mov %0, %%cr4" : : "r"(cr4)); asm volatile("mov %0, %%cr4" : : "r"(cr4));
asm volatile("fninit"); asm volatile("fninit");
// 3. Load the shared GDT and properly reload all segments (including CS=0x08)
extern struct gdt_ptr gdtr; extern struct gdt_ptr gdtr;
extern void gdt_flush(uint64_t); extern void gdt_flush(uint64_t);
gdt_flush((uint64_t)&gdtr); gdt_flush((uint64_t)&gdtr);
// 4. Load per-CPU TSS
gdt_load_ap_tss(my_id); gdt_load_ap_tss(my_id);
// 5. Load the shared IDT
extern void idt_load(void); extern void idt_load(void);
idt_load(); idt_load();
// 6. Load the kernel page tables (same CR3 as BSP — shared kernel space)
uint64_t kernel_cr3 = paging_get_pml4_phys(); uint64_t kernel_cr3 = paging_get_pml4_phys();
asm volatile("mov %0, %%cr3" : : "r"(kernel_cr3)); asm volatile("mov %0, %%cr3" : : "r"(kernel_cr3));
// 7. Enable LAPIC on this core so it can receive IPIs
extern void lapic_enable(void); extern void lapic_enable(void);
lapic_enable(); lapic_enable();
// 8. Mark ourselves as online
cpu_states[my_id].online = true; cpu_states[my_id].online = true;
serial_write("[SMP] AP "); 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_num(cpu_states[my_id].lapic_id);
serial_write(")\n"); serial_write(")\n");
// 9. Initialize the current_process pointer for this CPU process_t *ap_idle = process_create(NULL, false);
// 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
ap_idle->cpu_affinity = my_id; ap_idle->cpu_affinity = my_id;
process_set_current_for_cpu(my_id, ap_idle); 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"); asm volatile("sti");
// Idle loop — APs halt and wait for IPI work_queue_drain_loop();
for (;;) { asm volatile("hlt"); }
} }
// --- SMP Initialization --- // --- SMP Initialization ---
uint32_t smp_init(struct limine_smp_response *smp_resp) { uint32_t smp_init(struct limine_smp_response *smp_resp) {
if (!smp_resp || smp_resp->cpu_count <= 1) { if (!smp_resp || smp_resp->cpu_count <= 1) {
// Single CPU system — just set up the BSP entry
total_cpus = 1; total_cpus = 1;
cpu_states = (cpu_state_t *)kmalloc(sizeof(cpu_state_t)); cpu_states = (cpu_state_t *)kmalloc(sizeof(cpu_state_t));
if (!cpu_states) return 1; 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_num(bsp_lapic_id);
serial_write("\n"); serial_write("\n");
// Allocate per-CPU state array
cpu_states = (cpu_state_t *)kmalloc(total_cpus * sizeof(cpu_state_t)); cpu_states = (cpu_state_t *)kmalloc(total_cpus * sizeof(cpu_state_t));
if (!cpu_states) { if (!cpu_states) {
serial_write("[SMP] ERROR: Failed to allocate CPU state array!\n"); 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); extern void mem_memset(void *, int, size_t);
mem_memset(cpu_states, 0, total_cpus * sizeof(cpu_state_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); gdt_init_ap_tss(total_cpus);
// Fill in CPU state and start APs
uint32_t bsp_index = 0; uint32_t bsp_index = 0;
for (uint32_t i = 0; i < total_cpus; i++) { for (uint32_t i = 0; i < total_cpus; i++) {
struct limine_smp_info *cpu = smp_resp->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; cpu_states[i].lapic_id = cpu->lapic_id;
if (cpu->lapic_id == bsp_lapic_id) { if (cpu->lapic_id == bsp_lapic_id) {
// This is the BSP — already running
cpu_states[i].online = true; cpu_states[i].online = true;
bsp_index = i; bsp_index = i;
serial_write("[SMP] BSP CPU "); 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_num(cpu->lapic_id);
serial_write(") online\n"); serial_write(") online\n");
} else { } else {
// Allocate a kernel stack for this AP
void *ap_stack = kmalloc_aligned(65536, 65536); void *ap_stack = kmalloc_aligned(65536, 65536);
if (!ap_stack) { if (!ap_stack) {
serial_write("[SMP] ERROR: Failed to allocate AP stack!\n"); 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].kernel_stack_alloc = ap_stack;
cpu_states[i].online = false; cpu_states[i].online = false;
// Set extra_argument so the AP knows its index
cpu->extra_argument = i; 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("[SMP] Starting AP ");
serial_write_num(i); serial_write_num(i);
serial_write(" (LAPIC "); serial_write(" (LAPIC ");
serial_write_num(cpu->lapic_id); serial_write_num(cpu->lapic_id);
serial_write(")...\n"); 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); __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; volatile uint32_t timeout = 10000000;
uint32_t online_count = 0; uint32_t online_count = 0;
while (timeout-- > 0) { while (timeout-- > 0) {

View file

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

View file

@ -8,6 +8,9 @@
#include <stdbool.h> #include <stdbool.h>
#include "stdlib.h" #include "stdlib.h"
// External syscalls from libc
extern void sys_parallel_run(void (*fn)(void*), void **args, int count);
// ========= // =========
// Constants // 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_v1[GRID_3D][GRID_3D];
static bool surf_v2[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; static bool surface_needs_eval = true;
// Cached trig values for performance // 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) { static void gfb_clear(uint32_t c) {
int total = graph_w * graph_h; 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) { static void gfb_pixel(int x, int y, uint32_t c) {
@ -865,6 +874,7 @@ typedef struct {
int start_j, end_j; int start_j, end_j;
double range; double range;
double step; double step;
double z_scale;
} eval_job_t; } eval_job_t;
static void eval_3d_explicit_job(void *arg) { static void eval_3d_explicit_job(void *arg) {
@ -923,73 +933,81 @@ static void eval_3d_implicit_job(void *arg) {
} }
} }
// 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]) {
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]);
}
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]);
}
}
}
}
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) { static void render_3d_explicit(void) {
double step = range_3d * 2.0 / (GRID_3D - 1); double step = range_3d * 2.0 / (GRID_3D - 1);
double zmin = 1e30, zmax = -1e30; 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++) {
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) {
// 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);
}
}
}
}
static void render_3d_implicit(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) { 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; int num_chunks = 4;
eval_job_t jobs[4]; eval_job_t jobs[4];
void *job_args[4]; void *job_args[4];
@ -1003,10 +1021,99 @@ static void render_3d_implicit(void) {
job_args[c] = &jobs[c]; 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); 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 // Compute min/max for coloring based on what's visible
for (int j = 0; j < GRID_3D; j++) { for (int j = 0; j < GRID_3D; j++) {
for (int i = 0; i < GRID_3D; i++) { for (int i = 0; i < GRID_3D; i++) {
@ -1021,45 +1128,20 @@ static void render_3d_implicit(void) {
} }
} }
// Draw mesh for both surfaces // Pass 3: Parallel Drawing
for (int j = 0; j < GRID_3D; j++) { {
for (int i = 0; i < GRID_3D; i++) { int num_chunks = 4;
for (int s = 0; s < 2; s++) { draw_job_t jobs[4];
bool *v = (s == 0) ? surf_v1[j] : surf_v2[j]; void *job_args[4];
double *z = (s == 0) ? surf_z1[j] : surf_z2[j]; int rows_per_chunk = GRID_3D / num_chunks;
if (!v[i]) continue; for (int c = 0; c < num_chunks; c++) {
int sx0, sy0; jobs[c].start_j = c * rows_per_chunk;
project_3d(surf_x[j][i], z[i], surf_y_3d[j][i], &sx0, &sy0); jobs[c].end_j = (c == num_chunks - 1) ? GRID_3D : (c + 1) * rows_per_chunk;
uint32_t col = color_by_height(z[i], zmin, zmax); jobs[c].zmin = zmin;
jobs[c].zmax = zmax;
if (i+1 < GRID_3D && ((s == 0 && surf_v1[j][i+1]) || (s == 1 && surf_v2[j][i+1]))) { job_args[c] = &jobs[c];
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);
}
}
}
} }
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(); 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); 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; 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++) { for (int i = 0; i < state->item_count; i++) {
int row = i / EXPLORER_COLS; int row = i / EXPLORER_COLS;
@ -1010,11 +1010,8 @@ static void explorer_paint(Window *win) {
} }
if (dirty.active) { graphics_pop_clipping(); // Pop content clipping
graphics_set_clipping(dirty.x, dirty.y, dirty.w, dirty.h); graphics_pop_clipping(); // Pop main window clipping
} else {
graphics_clear_clipping();
}
if (state->drive_menu_visible) { if (state->drive_menu_visible) {
int menu_x = win->x + 4; 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 #define MAX_FB_HEIGHT 2048
static uint32_t g_back_buffer[MAX_FB_WIDTH * MAX_FB_HEIGHT] __attribute__((aligned(4096))); 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; #define MAX_RENDER_CPUS 32
static bool g_clip_enabled = false; #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); extern uint32_t smp_this_cpu_id(void);
#define MAX_RENDER_CPUS 32
static uint32_t *g_render_target[MAX_RENDER_CPUS] = {0}; static uint32_t *g_render_target[MAX_RENDER_CPUS] = {0};
static int g_rt_width[MAX_RENDER_CPUS] = {0}; static int g_rt_width[MAX_RENDER_CPUS] = {0};
static int g_rt_height[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 (!g_fb) return;
if (x < 0 || x >= (int)g_fb->width || y < 0 || y >= (int)g_fb->height) return; if (x < 0 || x >= (int)g_fb->width || y < 0 || y >= (int)g_fb->height) return;
if (g_clip_enabled) { if (g_clip_enabled[cpu]) {
if (x < g_clip_x || x >= g_clip_x + g_clip_w || int ptr = g_clip_stack_ptr[cpu];
y < g_clip_y || y >= g_clip_y + g_clip_h) { 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; 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_fb) return;
if (g_clip_enabled) { if (g_clip_enabled[cpu]) {
if (x1 < g_clip_x) x1 = g_clip_x; int ptr = g_clip_stack_ptr[cpu];
if (y1 < g_clip_y) y1 = g_clip_y; if (x1 < g_clip_stack_x[cpu][ptr]) x1 = g_clip_stack_x[cpu][ptr];
if (x2 > g_clip_x + g_clip_w) x2 = g_clip_x + g_clip_w; if (y1 < g_clip_stack_y[cpu][ptr]) y1 = g_clip_stack_y[cpu][ptr];
if (y2 > g_clip_y + g_clip_h) y2 = g_clip_y + g_clip_h; 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; 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++) { for (int r = 0; r < h; r++) {
int g_y = y + r; int g_y = y + r;
int g_x = x + c; if (g_y < 0 || g_y >= sh) continue;
if (g_clip_enabled) { uint32_t cpu = smp_this_cpu_id();
if (g_x < g_clip_x || g_x >= g_clip_x + g_clip_w || if (g_clip_enabled[cpu]) {
g_y < g_clip_y || g_y >= g_clip_y + g_clip_h) { int ptr = g_clip_stack_ptr[cpu];
continue; 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; 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(); uint32_t cpu = smp_this_cpu_id();
bool has_rt = (cpu < MAX_RENDER_CPUS && g_render_target[cpu]); bool has_rt = (cpu < MAX_RENDER_CPUS && g_render_target[cpu]);
if (g_clip_enabled && !has_rt) { if (g_clip_enabled[cpu] && !has_rt) {
if (x + 8 <= g_clip_x || x >= g_clip_x + g_clip_w || int ptr = g_clip_stack_ptr[cpu];
y + 8 <= g_clip_y || y >= g_clip_y + g_clip_h) { 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; 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(); uint32_t cpu = smp_this_cpu_id();
bool has_rt = (cpu < MAX_RENDER_CPUS && g_render_target[cpu]); bool has_rt = (cpu < MAX_RENDER_CPUS && g_render_target[cpu]);
if (g_clip_enabled && !has_rt) { if (g_clip_enabled[cpu] && !has_rt) {
if (x + 8 <= g_clip_x || x >= g_clip_x + g_clip_w || int ptr = g_clip_stack_ptr[cpu];
y + 8 <= g_clip_y || y >= g_clip_y + g_clip_h) { 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; return;
} }
} }
@ -640,9 +656,11 @@ void draw_desktop_background(void) {
if (g_use_image && g_bg_image) { if (g_use_image && g_bg_image) {
// Draw wallpaper image (stretch/scale to screen) // Draw wallpaper image (stretch/scale to screen)
int x1 = 0, y1 = 0, x2 = g_fb->width, y2 = g_fb->height; int x1 = 0, y1 = 0, x2 = g_fb->width, y2 = g_fb->height;
if (g_clip_enabled) { uint32_t cpu = smp_this_cpu_id();
x1 = g_clip_x; y1 = g_clip_y; if (g_clip_enabled[cpu]) {
x2 = g_clip_x + g_clip_w; y2 = g_clip_y + g_clip_h; 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++) { for (int y = y1; y < y2; y++) {
int src_y = y * g_bg_image_h / (int)g_fb->height; 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) { } else if (g_use_pattern) {
// Optimized tiled pattern: only draw within the clipping/dirty rect // Optimized tiled pattern: only draw within the clipping/dirty rect
int x1 = 0, y1 = 0, x2 = g_fb->width, y2 = g_fb->height; int x1 = 0, y1 = 0, x2 = g_fb->width, y2 = g_fb->height;
if (g_clip_enabled) { uint32_t cpu = smp_this_cpu_id();
x1 = g_clip_x; y1 = g_clip_y; if (g_clip_enabled[cpu]) {
x2 = g_clip_x + g_clip_w; y2 = g_clip_y + g_clip_h; 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++) { 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 (w < 0) w = 0;
if (h < 0) h = 0; if (h < 0) h = 0;
g_clip_x = x; uint32_t cpu = smp_this_cpu_id();
g_clip_y = y; g_clip_stack_x[cpu][0] = x;
g_clip_w = w; g_clip_stack_y[cpu][0] = y;
g_clip_h = h; g_clip_stack_w[cpu][0] = w;
g_clip_enabled = true; 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) { 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) { void graphics_blit_buffer(uint32_t *src, int dst_x, int dst_y, int w, int h) {
if (!g_fb || !src) return; if (!g_fb || !src) return;
int sw = get_screen_width(); int sw = get_screen_width();
int sh = get_screen_height(); int sh = get_screen_height();
for (int y = 0; y < h; y++) { uint32_t cpu = smp_this_cpu_id();
int vy = dst_y + y; int cx1 = 0, cy1 = 0, cx2 = sw, cy2 = sh;
if (vy < 0 || vy >= sh) continue; 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 x1 = dst_x, y1 = dst_y, x2 = dst_x + w, y2 = dst_y + h;
int vx = dst_x + x; if (x1 < cx1) x1 = cx1;
if (vx < 0 || vx >= sw) continue; 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) { 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 // Clipping
void graphics_set_clipping(int x, int y, int w, int h); void graphics_set_clipping(int x, int y, int w, int h);
void graphics_push_clipping(int x, int y, int w, int h);
void graphics_pop_clipping(void);
void graphics_clear_clipping(void); void graphics_clear_clipping(void);
// Font access (requires font_manager.h for ttf_font_t) // Font access (requires font_manager.h for ttf_font_t)

View file

@ -17,6 +17,8 @@
#include "userland/stb_image.h" #include "userland/stb_image.h"
#include "memory_manager.h" #include "memory_manager.h"
#include "disk.h" #include "disk.h"
#include "../sys/work_queue.h"
#include "../sys/smp.h"
// Hello developer, // Hello developer,
@ -98,6 +100,14 @@ static int drag_offset_y = 0;
bool is_dragging_file = false; bool is_dragging_file = false;
static char drag_file_path[FAT32_MAX_PATH]; static char drag_file_path[FAT32_MAX_PATH];
static int drag_icon_type = 0; 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_x = 0;
static int drag_start_y = 0; static int drag_start_y = 0;
static int drag_icon_orig_x = 0; static int drag_icon_orig_x = 0;
@ -111,10 +121,8 @@ static int window_count = 0;
// Redraw system // Redraw system
static bool force_redraw = true; static bool force_redraw = true;
static uint32_t timer_ticks = 0; static uint32_t timer_ticks = 0;
static int desktop_refresh_timer = 0;
// Cursor state // Cursor state
static bool cursor_visible = true;
static int last_cursor_x = 400; static int last_cursor_x = 400;
static int last_cursor_y = 300; 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); 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) { void draw_window(Window *win) {
if (!win->visible) return; if (!win->visible) return;
@ -1240,6 +1227,7 @@ static void erase_cursor(int x, int y) {
} }
// --- Clock --- // --- Clock ---
static uint8_t rtc_read(uint8_t reg) { static uint8_t rtc_read(uint8_t reg) {
outb(0x70, reg); outb(0x70, reg);
return inb(0x71); return inb(0x71);
@ -1274,60 +1262,43 @@ static void draw_clock(int x, int y) {
} }
// --- Main Paint Function --- // --- 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 sw = get_screen_width();
int sh = get_screen_height(); int sh = get_screen_height();
uint64_t rflags; int cx = 0, cy = y_start, cw = sw, ch = y_end - y_start;
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) { if (dirty.active) {
int d_h = 60; if (cx < dirty.x) { cw -= (dirty.x - cx); cx = dirty.x; }
int d_y = sh - d_h - 6; if (cy < dirty.y) { ch -= (dirty.y - cy); cy = dirty.y; }
int d_item_sz = 48; if (cx + cw > dirty.x + dirty.w) cw = dirty.x + dirty.w - cx;
int d_space = 10; if (cy + ch > dirty.y + dirty.h) ch = dirty.y + dirty.h - cy;
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;
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) if (cw <= 0 || ch <= 0) return;
graphics_set_clipping(cx, cy, cw, ch);
if (pass == 1) {
draw_desktop_background(); draw_desktop_background();
// Draw Desktop Icons
for (int i = 0; i < desktop_icon_count; i++) { for (int i = 0; i < desktop_icon_count; i++) {
DesktopIcon *icon = &desktop_icons[i]; DesktopIcon *icon = &desktop_icons[i];
if (dirty.active) { if (icon->y + 85 <= cy || icon->y >= cy + ch) continue;
if (icon->x + 80 <= dirty.x || icon->x >= dirty.x + dirty.w || if (dirty.active && (icon->x + 85 <= dirty.x || icon->x >= dirty.x + dirty.w)) continue;
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); if (icon->type == 1) draw_folder_icon(icon->x, icon->y, icon->name);
else if (icon->type == 2) { else if (icon->type == 2) {
// App icon - strip .shortcut for display char label[64]; int len = 0;
char label[64];
int len = 0;
while(icon->name[len] && len < 63) { label[len] = icon->name[len]; len++; } while(icon->name[len] && len < 63) { label[len] = icon->name[len]; len++; }
label[len] = 0; label[len] = 0;
if (len > 9 && str_ends_with(label, ".shortcut")) { if (len > 9 && str_ends_with(label, ".shortcut")) label[len-9] = 0;
label[len-9] = 0;
}
if (str_starts_with(icon->name, "Notepad")) draw_notepad_icon(icon->x, icon->y, label); 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, "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, "Terminal")) draw_terminal_icon(icon->x, icon->y, label);
@ -1343,8 +1314,7 @@ void wm_paint(void) {
if (str_ends_with(icon->name, ".elf")) draw_elf_icon(icon->x, icon->y, icon->name); 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 (str_ends_with(icon->name, ".pnt")) draw_paint_icon(icon->x, icon->y, icon->name);
else if (is_image_file(icon->name)) { else if (is_image_file(icon->name)) {
char full_path[128] = "/Desktop/"; 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;
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_image_icon(icon->x, icon->y, full_path);
draw_icon_label(icon->x, icon->y, icon->name); draw_icon_label(icon->x, icon->y, icon->name);
} }
@ -1353,196 +1323,202 @@ void wm_paint(void) {
} }
} }
// 3. Windows - sort by z-index and draw for (int i = 0; i < sorted_window_count_cache; i++) {
int local_window_count = window_count; Window *win = sorted_windows_cache[i];
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;
}
}
}
for (int i = 0; i < local_window_count; i++) {
Window *win = sorted_windows[i];
if (!win || !win->visible) continue; if (!win || !win->visible) continue;
if (win->y + win->h <= cy || win->y >= cy + ch) continue;
if (dirty.active && !win->focused) { if (dirty.active && !win->focused && (win->x + win->w <= dirty.x || win->x >= dirty.x + dirty.w)) continue;
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); draw_window(win);
} }
} else if (pass == 2) {
if (0 < cy + ch && 30 > cy) {
draw_rect(0, 0, sw, 30, COLOR_TOPBAR_BG); draw_rect(0, 0, sw, 30, COLOR_TOPBAR_BG);
draw_boredos_logo(8, 8, 1); draw_boredos_logo(8, 8, 1);
draw_clock(sw - 80, 12); draw_clock(sw - 80, 12);
}
if (start_menu_open) { if (start_menu_open && 40 < cy + ch && 125 > cy) {
int menu_h = 85; draw_rounded_rect_filled(8, 40, 160, 85, 8, COLOR_DARK_PANEL);
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, 48, "About BoredOS", COLOR_DARK_TEXT);
draw_string(20, 68, "Settings", COLOR_DARK_TEXT); draw_string(20, 68, "Settings", COLOR_DARK_TEXT);
draw_string(20, 88, "Shutdown", COLOR_DARK_TEXT); draw_string(20, 88, "Shutdown", COLOR_DARK_TEXT);
draw_string(20, 108, "Restart", COLOR_DARK_TEXT); draw_string(20, 108, "Restart", COLOR_DARK_TEXT);
} }
int dock_h = 60; int dock_h = 60, dock_y = sh - dock_h - 6;
int dock_y = sh - dock_h - 6; if (dock_y < cy + ch && dock_y + dock_h > cy) {
int dock_item_size = 48; int d_item_sz = 48, d_space = 10, d_total_w = 11 * (d_item_sz + d_space);
int dock_spacing = 10; int d_bg_x = (sw - d_total_w) / 2 - 12, d_bg_w = d_total_w + 24;
int total_dock_width = 11 * (dock_item_size + dock_spacing); draw_rounded_rect_blurred(d_bg_x, dock_y, d_bg_w, dock_h, 18, COLOR_DOCK_BG, 5, 140);
int dock_bg_x = (sw - total_dock_width) / 2 - 12; int dx = (sw - d_total_w) / 2, dy = dock_y + 6;
int dock_bg_w = total_dock_width + 24; 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);
}
// 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) { if (desktop_menu_visible) {
int menu_w = 140; 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; 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) { if (desktop_menu_target_icon != -1) {
bool can_paste = explorer_clipboard_has_content(); 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, "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, "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 * 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 * 3, "Delete", COLOR_TRAFFIC_RED);
draw_string(desktop_menu_x + 10, desktop_menu_y + 5 + item_h * 4, "Rename", COLOR_WHITE); draw_string(desktop_menu_x + 10, desktop_menu_y + 5 + item_h * 4, "Rename", COLOR_WHITE);
} else { } else {
bool can_paste = explorer_clipboard_has_content(); 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, "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, "New Folder", 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 * 2, "Paste", cp ? COLOR_WHITE : COLOR_DKGRAY);
}
} }
} }
// Desktop Dialogs (dark mode)
if (desktop_dialog_state != 0) { if (desktop_dialog_state != 0) {
int dlg_w = 300; int dlg_h = 110; int dlg_w = 300, dlg_h = 110, dg_x = (sw - dlg_w)/2, dg_y = (sh - dlg_h)/2;
int dlg_x = (sw - dlg_w) / 2; if (dg_y < cy + ch && dg_y + dlg_h > cy) {
int dlg_y = (sh - dlg_h) / 2; 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");
draw_rounded_rect_filled(dlg_x, dlg_y, dlg_w, dlg_h, 8, COLOR_DARK_PANEL); const char *btn = (desktop_dialog_state == 0) ? "Rename" : "Create";
draw_string(dg_x + 10, dg_y + 10, title, COLOR_WHITE);
const char *title = "Rename"; draw_rounded_rect_filled(dg_x + 10, dg_y + 35, 280, 20, 4, COLOR_DARK_BG);
const char *btn_text = "Rename"; draw_string(dg_x + 15, dg_y + 40, desktop_dialog_input, COLOR_WHITE);
if (desktop_dialog_state == 1) { title = "Create New File"; btn_text = "Create"; } 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;
else if (desktop_dialog_state == 2) { title = "Create New Folder"; btn_text = "Create"; } 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_string(dlg_x + 10, dlg_y + 10, title, 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);
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) { if (msg_box_visible) {
int mw = 320; int mw = 320, mh = 100, m_x = (sw - mw)/2, m_y = (sh - mh)/2;
int mh = 100; if (m_y < cy + ch && m_y + mh > cy) {
int mx = (sw - mw) / 2; draw_rounded_rect_filled(m_x, m_y, mw, mh, 8, COLOR_DARK_PANEL);
int my = (sh - mh) / 2; 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(mx, my, mw, mh, 8, COLOR_DARK_PANEL); draw_rounded_rect_filled(m_x + mw/2 - 30, m_y + 70, 60, 20, 4, COLOR_DARK_BORDER);
draw_string(mx + 15, my + 10, msg_box_title, COLOR_DARK_TEXT); draw_string(m_x + mw/2 - 10, m_y + 75, "OK", COLOR_WHITE);
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) { if (notif_active) {
int nx = sw - 400 + notif_x_offset; int nx = sw - 400 + notif_x_offset, ny = 40, nw = 380, nh = 50;
int ny = 40; if (ny < cy + ch && ny + nh > cy) {
int nw = 380;
int nh = 50;
draw_rounded_rect_filled(nx, ny, nw, nh, 8, COLOR_DARK_PANEL); 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 + 10, "Screenshot", COLOR_DARK_TEXT);
draw_string(nx + 15, ny + 30, notif_text, COLOR_DKGRAY); 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 (wm_custom_paint_hook) wm_custom_paint_hook();
if (is_dragging_file) { 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..."); 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 if (drag_icon_type == 2) draw_icon(mx - 20, my - 20, "Moving...");
else draw_document_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) 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, 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();
}
}
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;
}
}
}
// Memory barrier to ensure APs see the sorted window list correctly
asm volatile("" ::: "memory");
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");
}
// 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");
}
graphics_clear_clipping();
draw_cursor(mx, my); draw_cursor(mx, my);
last_cursor_x = mx; last_cursor_x = mx;
last_cursor_y = my; last_cursor_y = my;
// Flip the buffer - display the rendered frame atomically
graphics_flip_buffer(); graphics_flip_buffer();
graphics_clear_dirty_no_lock(); graphics_clear_dirty_no_lock();
// Restore IRQs
wm_lock_release(rflags); 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) { void wm_bring_to_front_locked(Window *win) {
for (int i = 0; i < window_count; i++) { for (int i = 0; i < window_count; i++) {
all_windows[i]->focused = false; all_windows[i]->focused = false;
@ -1725,7 +1701,8 @@ void wm_handle_click(int x, int y) {
if (desktop_snap_to_grid) { if (desktop_snap_to_grid) {
int col = (desktop_icons[new_idx].x - 20 + 40) / 80; int col = (desktop_icons[new_idx].x - 20 + 40) / 80;
int row = (desktop_icons[new_idx].y - 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].x = 20 + col * 80;
desktop_icons[new_idx].y = 20 + row * 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) { if (desktop_snap_to_grid) {
int col = (desktop_icons[i].x - 20 + 40) / 80; int col = (desktop_icons[i].x - 20 + 40) / 80;
int row = (desktop_icons[i].y - 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].x = 20 + col * 80;
desktop_icons[i].y = 20 + row * 80; desktop_icons[i].y = 20 + row * 80;
} }