diff --git a/Makefile b/Makefile index 13e7c50..30d3d10 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,8 @@ KERNEL_CPP = kernel/entry.cpp kernel/main.cpp kernel/serial.cpp kernel/fs.cpp \ kernel/scheduler/scheduler.cpp \ kernel/interrupt/gdt.cpp kernel/interrupt/idt.cpp \ kernel/interrupt/pic.cpp kernel/interrupt/pit.cpp \ - graphics/context.cpp graphics/draw.cpp \ + kernel/graphics/layer.cpp \ + graphics/context.cpp graphics/draw.cpp graphics/rect.cpp \ fonts/pixel_font.cpp KERNEL_ASM = kernel/scheduler/context_switch.S kernel/interrupt/isr.S kernel/interrupt/idt_helpers.S KERNEL_OBJ = $(KERNEL_CPP:%.cpp=build/%.o) $(KERNEL_ASM:%.S=build/%.o) @@ -48,7 +49,7 @@ all: _bd $(EFI_OBJ) $(BOOT_OBJ) $(KERNEL_OBJ) _bd: @mkdir -p build/graphics build/kernel build/fonts build/kernel/memory \ - build/kernel/scheduler build/kernel/interrupt \ + build/kernel/scheduler build/kernel/interrupt build/kernel/graphics \ build/efi/lib build/efi/lib/x86_64 build/efi/lib/runtime build/efi/gnuefi $(EFI_CRT0_OBJ): efi/gnuefi/crt0-efi-x86_64.S | _bd diff --git a/graphics/rect.cpp b/graphics/rect.cpp new file mode 100644 index 0000000..08d1e98 --- /dev/null +++ b/graphics/rect.cpp @@ -0,0 +1,61 @@ +#include + +void gfx_fill_rect(EFI_GRAPHICS_OUTPUT_BLT_PIXEL* buf, + UINT32 buf_w, UINT32 buf_h, + UINT32 x, UINT32 y, UINT32 w, UINT32 h, + EFI_GRAPHICS_OUTPUT_BLT_PIXEL color) { + if (x >= buf_w || y >= buf_h) return; + + UINT32 x2 = x + w; + UINT32 y2 = y + h; + if (x2 > buf_w) x2 = buf_w; + if (y2 > buf_h) y2 = buf_h; + + for (UINT32 row = y; row < y2; row++) { + EFI_GRAPHICS_OUTPUT_BLT_PIXEL* p = buf + (buf_w * row) + x; + for (UINT32 col = x; col < x2; col++) { + p->Blue = color.Blue; + p->Green = color.Green; + p->Red = color.Red; + p->Reserved = color.Reserved; + p++; + } + } +} + +void gfx_draw_rect(EFI_GRAPHICS_OUTPUT_BLT_PIXEL* buf, + UINT32 buf_w, UINT32 buf_h, + UINT32 x, UINT32 y, UINT32 w, UINT32 h, + EFI_GRAPHICS_OUTPUT_BLT_PIXEL color) { + if (x >= buf_w || y >= buf_h) return; + + UINT32 x2 = x + w; + UINT32 y2 = y + h; + if (x2 > buf_w) x2 = buf_w; + if (y2 > buf_h) y2 = buf_h; + + // Top edge + for (UINT32 col = x; col < x2; col++) { + EFI_GRAPHICS_OUTPUT_BLT_PIXEL* p = buf + (buf_w * y) + col; + p->Blue = color.Blue; p->Green = color.Green; p->Red = color.Red; p->Reserved = color.Reserved; + } + // Bottom edge + if (y2 - 1 < buf_h) { + for (UINT32 col = x; col < x2; col++) { + EFI_GRAPHICS_OUTPUT_BLT_PIXEL* p = buf + (buf_w * (y2 - 1)) + col; + p->Blue = color.Blue; p->Green = color.Green; p->Red = color.Red; p->Reserved = color.Reserved; + } + } + // Left edge + for (UINT32 row = y; row < y2; row++) { + EFI_GRAPHICS_OUTPUT_BLT_PIXEL* p = buf + (buf_w * row) + x; + p->Blue = color.Blue; p->Green = color.Green; p->Red = color.Red; p->Reserved = color.Reserved; + } + // Right edge + if (x2 - 1 < buf_w) { + for (UINT32 row = y; row < y2; row++) { + EFI_GRAPHICS_OUTPUT_BLT_PIXEL* p = buf + (buf_w * row) + (x2 - 1); + p->Blue = color.Blue; p->Green = color.Green; p->Red = color.Red; p->Reserved = color.Reserved; + } + } +} diff --git a/include/graphics/layer.h b/include/graphics/layer.h new file mode 100644 index 0000000..8c828a2 --- /dev/null +++ b/include/graphics/layer.h @@ -0,0 +1,40 @@ +#pragma once + +#include + +#define LAYER_MAX 32 +#define LAYER_NAME_LEN 32 + +typedef enum { + LAYER_TYPE_DESKTOP, + LAYER_TYPE_WINDOW, + LAYER_TYPE_MOUSE, + LAYER_TYPE_OVERLAY, +} layer_type_t; + +typedef struct layer { + UINT32 id; + char name[LAYER_NAME_LEN]; + layer_type_t type; + int x, y; + UINT32 w, h; + int z; + bool visible; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL* buffer; + struct layer* next; +} layer_t; + +// Initialization — allocates back buffer, registers keyboard handler +void layer_init(void); + +// Layer management +layer_t* layer_create(const char* name, layer_type_t type, UINT32 w, UINT32 h); +void layer_destroy(layer_t* layer); +void layer_set_z(layer_t* layer, int z); +void layer_set_pos(layer_t* layer, int x, int y); +void layer_set_visible(layer_t* layer, bool visible); +layer_t* layer_get_by_id(UINT32 id); +layer_t* layer_get_focused(void); + +// Compositor task entry +void layer_compositor_task(void); diff --git a/include/graphics/rect.h b/include/graphics/rect.h new file mode 100644 index 0000000..1a05016 --- /dev/null +++ b/include/graphics/rect.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +void gfx_fill_rect(EFI_GRAPHICS_OUTPUT_BLT_PIXEL* buf, + UINT32 buf_w, UINT32 buf_h, + UINT32 x, UINT32 y, UINT32 w, UINT32 h, + EFI_GRAPHICS_OUTPUT_BLT_PIXEL color); + +void gfx_draw_rect(EFI_GRAPHICS_OUTPUT_BLT_PIXEL* buf, + UINT32 buf_w, UINT32 buf_h, + UINT32 x, UINT32 y, UINT32 w, UINT32 h, + EFI_GRAPHICS_OUTPUT_BLT_PIXEL color); diff --git a/kernel/graphics/layer.cpp b/kernel/graphics/layer.cpp new file mode 100644 index 0000000..084c697 --- /dev/null +++ b/kernel/graphics/layer.cpp @@ -0,0 +1,319 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// --- Layer list (sorted by z, lowest first) --- + +static layer_t g_layers[LAYER_MAX]; +static UINT32 g_layer_count = 0; +static layer_t* g_layer_list = NULL; + +// Compositor back buffer +static EFI_GRAPHICS_OUTPUT_BLT_PIXEL* g_back_buffer = NULL; + +// Focus tracking +static layer_t* g_focused = NULL; + +// Shift+F10 state (set by IRQ handler, consumed by compositor) +static volatile bool g_shift_held = false; +static volatile bool g_switch_pending = false; +static volatile layer_t* g_switch_target = NULL; + +// PS/2 scan code set 1 +#define PS2_F10 0x44 +#define PS2_LSHIFT 0x2A +#define PS2_RSHIFT 0x36 +#define PS2_BREAK_BIT 0x80 + +// Forward declare +static void layer_insert_sorted(layer_t* layer); +static void layer_remove(layer_t* layer); +static layer_t* find_next_window(layer_t* from); + +// --- PS/2 keyboard IRQ handler --- + +static void ps2_irq_handler(trap_frame* frame) { + (void)frame; + pic_send_eoi(1); + + UINT8 sc; + ASM("inb %1, %0" : "=a"(sc) : "Nd"((UINT16)0x60)); + + bool is_break = (sc & PS2_BREAK_BIT) != 0; + UINT8 code = sc & 0x7F; + + if (code == PS2_LSHIFT || code == PS2_RSHIFT) { + g_shift_held = !is_break; + return; + } + + if (!is_break && g_shift_held && code == PS2_F10) { + layer_t* next = find_next_window((layer_t*)g_focused); + if (next) { + g_switch_target = next; + g_switch_pending = true; + } + } +} + +// --- Layer management --- + +layer_t* layer_create(const char* name, layer_type_t type, UINT32 w, UINT32 h) { + if (g_layer_count >= LAYER_MAX) { + serial_write("LAYER: limit reached\n"); + return NULL; + } + + UINT32 id = g_layer_count++; + layer_t* layer = &g_layers[id]; + + layer->id = id; + layer->type = type; + layer->x = 0; + layer->y = 0; + layer->w = w; + layer->h = h; + layer->z = 0; + layer->visible = true; + layer->next = NULL; + + const char* s = name; + char* d = layer->name; + for (int i = 0; i < LAYER_NAME_LEN - 1 && *s; i++) { + *d++ = *s++; + } + *d = '\0'; + + UINTN buf_size = (UINTN)w * h * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL); + layer->buffer = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL*)kmalloc(buf_size); + if (!layer->buffer) { + serial_write("LAYER: buffer alloc failed for "); + serial_write(name); + serial_write("\n"); + g_layer_count--; + return NULL; + } + + EFI_GRAPHICS_OUTPUT_BLT_PIXEL* p = layer->buffer; + for (UINTN i = 0; i < (UINTN)w * h; i++) { + p->Blue = 0; p->Green = 0; p->Red = 0; p->Reserved = 0; + p++; + } + + layer_insert_sorted(layer); + + serial_write("LAYER: created '"); + serial_write(name); + serial_write("' id="); + serial_write_hex(id); + serial_write("\n"); + + return layer; +} + +void layer_destroy(layer_t* layer) { + if (!layer) return; + + layer_remove(layer); + + if (layer->buffer) { + kfree(layer->buffer); + layer->buffer = NULL; + } + + if (g_focused == layer) g_focused = NULL; + if (g_switch_target == layer) { + g_switch_target = NULL; + g_switch_pending = false; + } + + serial_write("LAYER: destroyed '"); + serial_write(layer->name); + serial_write("'\n"); +} + +layer_t* layer_get_by_id(UINT32 id) { + if (id >= LAYER_MAX) return NULL; + layer_t* l = &g_layers[id]; + return l->buffer ? l : NULL; +} + +layer_t* layer_get_focused(void) { + return g_focused; +} + +void layer_set_z(layer_t* layer, int z) { + if (!layer) return; + layer_remove(layer); + layer->z = z; + layer_insert_sorted(layer); +} + +void layer_set_pos(layer_t* layer, int x, int y) { + if (!layer) return; + layer->x = x; + layer->y = y; +} + +void layer_set_visible(layer_t* layer, bool visible) { + if (!layer) return; + layer->visible = visible; +} + +// --- Sorted insert/remove --- + +static void layer_insert_sorted(layer_t* layer) { + layer->next = NULL; + + if (!g_layer_list || layer->z < g_layer_list->z) { + layer->next = g_layer_list; + g_layer_list = layer; + return; + } + + layer_t* cur = g_layer_list; + while (cur->next && cur->next->z <= layer->z) { + cur = cur->next; + } + layer->next = cur->next; + cur->next = layer; +} + +static void layer_remove(layer_t* layer) { + if (!g_layer_list) return; + + if (g_layer_list == layer) { + g_layer_list = layer->next; + layer->next = NULL; + return; + } + + layer_t* cur = g_layer_list; + while (cur->next && cur->next != layer) { + cur = cur->next; + } + if (cur->next == layer) { + cur->next = layer->next; + layer->next = NULL; + } +} + +static layer_t* find_next_window(layer_t* from) { + layer_t* start = from ? from->next : g_layer_list; + if (!start) start = g_layer_list; + + layer_t* cur = start; + do { + if (cur->type == LAYER_TYPE_WINDOW && cur->visible) { + return cur; + } + cur = cur->next; + if (!cur) cur = g_layer_list; + } while (cur != start); + + return NULL; +} + +// --- Initialization --- + +void layer_init(void) { + UINT32 hr = g_gfx.hr; + UINT32 vr = g_gfx.vr; + + UINTN buf_size = (UINTN)hr * vr * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL); + g_back_buffer = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL*)kmalloc(buf_size); + if (!g_back_buffer) { + serial_write("LAYER: back buffer alloc FAILED\n"); + return; + } + + EFI_GRAPHICS_OUTPUT_BLT_PIXEL* p = g_back_buffer; + for (UINTN i = 0; i < (UINTN)hr * vr; i++) { + p->Blue = 0; p->Green = 0; p->Red = 0; p->Reserved = 0; + p++; + } + + // Register keyboard IRQ and unmask + idt_set_handler(PIC_IRQ_BASE + 1, ps2_irq_handler); + pic_unmask_irq(1); + + serial_write("LAYER: compositor init OK (back buffer = "); + serial_write_hex(buf_size); + serial_write(" bytes)\n"); +} + +// --- Compositor task --- + +void layer_compositor_task(void) { + serial_write("LAYER: compositor task running\n"); + + UINT32 hr = g_gfx.hr; + UINT32 vr = g_gfx.vr; + + while (1) { + // Process deferred Shift+F10 switch (safe: not inside IRQ) + if (g_switch_pending) { + g_switch_pending = false; + layer_t* target = (layer_t*)g_switch_target; + if (target && target->visible && target->type == LAYER_TYPE_WINDOW) { + g_focused = target; + layer_set_z(target, 99); + serial_write("LAYER: Shift+F10 -> '"); + serial_write(target->name); + serial_write("'\n"); + } + } + + // Clear back buffer + EFI_GRAPHICS_OUTPUT_BLT_PIXEL black = {0, 0, 0, 0}; + gfx_fill_rect(g_back_buffer, hr, vr, 0, 0, hr, vr, black); + + // Composite layers from lowest z to highest + layer_t* cur = g_layer_list; + while (cur) { + if (cur->visible && cur->buffer) { + int sx = 0, sy = 0; + int dx = cur->x, dy = cur->y; + UINT32 sw = cur->w, sh = cur->h; + + if (dx < 0) { sx = -dx; sw -= sx; dx = 0; } + if (dy < 0) { sy = -dy; sh -= sy; dy = 0; } + if (dx + (int)sw > (int)hr) sw = hr - dx; + if (dy + (int)sh > (int)vr) sh = vr - dy; + if (sw == 0 || sh == 0) { cur = cur->next; continue; } + + for (UINT32 row = 0; row < sh; row++) { + EFI_GRAPHICS_OUTPUT_BLT_PIXEL* src = cur->buffer + ((UINTN)cur->w * (sy + row)) + sx; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL* dst = g_back_buffer + ((UINTN)hr * (dy + row)) + dx; + for (UINT32 col = 0; col < sw; col++) { + dst->Blue = src->Blue; + dst->Green = src->Green; + dst->Red = src->Red; + dst->Reserved = src->Reserved; + src++; + dst++; + } + } + } + cur = cur->next; + } + + // Blit to screen + g_gfx.GOP->Blt( + g_gfx.GOP, + g_back_buffer, + EfiBltBufferToVideo, + 0, 0, 0, 0, + hr, vr, 0 + ); + + yield(); + } +} diff --git a/kernel/main.cpp b/kernel/main.cpp index bbea2fb..0c206b5 100644 --- a/kernel/main.cpp +++ b/kernel/main.cpp @@ -1,6 +1,8 @@ #include #include #include +#include +#include #include #include #include @@ -155,34 +157,53 @@ extern "C" void kernel_main() { // --- Multitasking demo --- serial_write("Sylva: creating tasks...\n"); - // Task A: prints messages — preemption handles time slicing - task_create("taskA", []() { - for (int i = 0; i < 10; i++) { - serial_write("[taskA] running iteration "); - serial_write_hex(i); - serial_write("\n"); - for (volatile int j = 0; j < 50000000; j++) {} - } - serial_write("[taskA] done\n"); - }); + // Init compositor (allocates back buffer, registers keyboard handler) + layer_init(); - // Task B: prints messages - task_create("taskB", []() { - for (int i = 0; i < 5; i++) { - serial_write("[taskB] hello from taskB #"); - serial_write_hex(i); - serial_write("\n"); - for (volatile int j = 0; j < 50000000; j++) {} - } - serial_write("[taskB] done\n"); - }); + // Create desktop layer (full screen, z=0) + layer_t* desktop = layer_create("desktop", LAYER_TYPE_DESKTOP, g_gfx.hr, g_gfx.vr); + if (desktop) { + layer_set_z(desktop, 0); + // Fill with dark blue + EFI_GRAPHICS_OUTPUT_BLT_PIXEL bg = {180, 80, 40, 0}; // BGRA: dark blue + gfx_fill_rect(desktop->buffer, g_gfx.hr, g_gfx.vr, 0, 0, g_gfx.hr, g_gfx.vr, bg); + layer_set_pos(desktop, 0, 0); + } - // Task C: short task - task_create("taskC", []() { - serial_write("[taskC] quick task\n"); - for (volatile int j = 0; j < 50000000; j++) {} - serial_write("[taskC] finished\n"); - }); + // Create window 1 (centered) + layer_t* win1 = layer_create("window_1", LAYER_TYPE_WINDOW, 300, 200); + if (win1) { + layer_set_pos(win1, (int)(g_gfx.hr / 2) - 150, (int)(g_gfx.vr / 2) - 100); + layer_set_z(win1, 1); + // Fill with gray + EFI_GRAPHICS_OUTPUT_BLT_PIXEL win_color = {200, 200, 200, 0}; + gfx_fill_rect(win1->buffer, 300, 200, 0, 0, 300, 200, win_color); + // White border + EFI_GRAPHICS_OUTPUT_BLT_PIXEL border = {255, 255, 255, 0}; + gfx_draw_rect(win1->buffer, 300, 200, 0, 0, 300, 200, border); + // Title bar (blue strip at top) + EFI_GRAPHICS_OUTPUT_BLT_PIXEL title_bar = {180, 100, 30, 0}; + gfx_fill_rect(win1->buffer, 300, 200, 0, 0, 300, 30, title_bar); + } + + // Create window 2 (offset from center) + layer_t* win2 = layer_create("window_2", LAYER_TYPE_WINDOW, 250, 180); + if (win2) { + layer_set_pos(win2, (int)(g_gfx.hr / 2) - 50, (int)(g_gfx.vr / 2) - 40); + layer_set_z(win2, 2); + // Fill with light green + EFI_GRAPHICS_OUTPUT_BLT_PIXEL win2_color = {180, 220, 140, 0}; + gfx_fill_rect(win2->buffer, 250, 180, 0, 0, 250, 180, win2_color); + // Dark border + EFI_GRAPHICS_OUTPUT_BLT_PIXEL border2 = {50, 50, 50, 0}; + gfx_draw_rect(win2->buffer, 250, 180, 0, 0, 250, 180, border2); + // Title bar + EFI_GRAPHICS_OUTPUT_BLT_PIXEL title2 = {160, 180, 100, 0}; + gfx_fill_rect(win2->buffer, 250, 180, 0, 0, 250, 28, title2); + } + + // Compositor task (replaces the old demo tasks) + task_create("compositor", layer_compositor_task); serial_write("Sylva: starting preemptive scheduler\n"); scheduler_run(); // never returns