diff --git a/src/sys/smp.c b/src/sys/smp.c index 4f4ede1..026c817 100644 --- a/src/sys/smp.c +++ b/src/sys/smp.c @@ -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) { diff --git a/src/sys/syscall.c b/src/sys/syscall.c index 310dd78..d4879a1 100644 --- a/src/sys/syscall.c +++ b/src/sys/syscall.c @@ -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,26 +632,25 @@ 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; if (ry + rh > (win->h - 20)) rh = (win->h - 20) - ry; - + 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; diff --git a/src/userland/gui/grapher.c b/src/userland/gui/grapher.c index f81ab95..628aa48 100644 --- a/src/userland/gui/grapher.c +++ b/src/userland/gui/grapher.c @@ -8,6 +8,9 @@ #include #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); } } diff --git a/src/wm/explorer.c b/src/wm/explorer.c index 7e8bd4a..88db9f5 100644 --- a/src/wm/explorer.c +++ b/src/wm/explorer.c @@ -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; diff --git a/src/wm/graphics.c b/src/wm/graphics.c index b2d085d..0c52643 100644 --- a/src/wm/graphics.c +++ b/src/wm/graphics.c @@ -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,18 +416,25 @@ 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; - - 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; - } - } + for (int r = 0; r < h; r++) { + int g_y = y + r; + if (g_y < 0 || g_y >= sh) 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; int dx = 0, dy = 0; if (c < radius && r < radius) { @@ -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; - - for (int x = 0; x < w; x++) { - int vx = dst_x + x; - if (vx < 0 || vx >= sw) continue; - - uint32_t pcol = src[y * w + x]; + 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]; + } + 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; + + 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; } } } diff --git a/src/wm/graphics.h b/src/wm/graphics.h index d98a558..2d8e09d 100644 --- a/src/wm/graphics.h +++ b/src/wm/graphics.h @@ -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) diff --git a/src/wm/wm.c b/src/wm/wm.c index bcb282f..915416f 100644 --- a/src/wm/wm.c +++ b/src/wm/wm.c @@ -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 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]); } - - 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); - } - - 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); - } + 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"); } - // 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); + // 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]); } - - // 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); + 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"); } - - // 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; }