From 3bece9e75e8992a99ccd0ff7c66bda8daae19367 Mon Sep 17 00:00:00 2001 From: pyao12 Date: Sat, 6 Jun 2026 11:32:08 +0800 Subject: [PATCH] =?UTF-8?q?[feat]=20=E7=AE=80=E6=98=93=E9=BC=A0=E6=A0=87?= =?UTF-8?q?=E9=A9=B1=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 2 +- devices/cursor/cursor.cpp | 75 +++++++++++++++++ devices/mouse/mouse.cpp | 170 ++++++++++++++++++++++++++++++++++++++ include/devices/cursor.h | 12 +++ include/devices/mouse.h | 48 +++++++++++ kernel/graphics/layer.cpp | 34 +++----- kernel/main.cpp | 9 ++ 7 files changed, 326 insertions(+), 24 deletions(-) create mode 100644 devices/cursor/cursor.cpp create mode 100644 devices/mouse/mouse.cpp create mode 100644 include/devices/cursor.h create mode 100644 include/devices/mouse.h diff --git a/Makefile b/Makefile index 99bcf29..8e36e19 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ BOOT_OBJ = build/boot.o KERNEL_ASMFLAGS = -Iinclude -Iefi/inc -ffreestanding -fno-stack-protector -fno-stack-check \ -fshort-wchar -mno-red-zone -fcf-protection=none -g -KERNEL_CPP := $(shell find kernel graphics fonts -name '*.cpp' -type f) +KERNEL_CPP := $(shell find kernel graphics fonts devices -name '*.cpp' -type f) KERNEL_ASM := $(shell find kernel -name '*.S' -type f) KERNEL_OBJ := $(KERNEL_CPP:%.cpp=build/%.o) $(KERNEL_ASM:%.S=build/%.o) diff --git a/devices/cursor/cursor.cpp b/devices/cursor/cursor.cpp new file mode 100644 index 0000000..fa1c842 --- /dev/null +++ b/devices/cursor/cursor.cpp @@ -0,0 +1,75 @@ +#include +#include +#include +#include +#include +#include + +// 经典箭头光标位图 (12x16) +// 0=透明, 1=白色填充, 2=黑色轮廓 +static const SUINT8 g_cursor_bitmap[CURSOR_H][CURSOR_W] = { + {2,0,0,0,0,0,0,0,0,0,0,0}, + {2,2,0,0,0,0,0,0,0,0,0,0}, + {2,1,2,0,0,0,0,0,0,0,0,0}, + {2,1,1,2,0,0,0,0,0,0,0,0}, + {2,1,1,1,2,0,0,0,0,0,0,0}, + {2,1,1,1,1,2,0,0,0,0,0,0}, + {2,1,1,1,1,1,2,0,0,0,0,0}, + {2,1,1,1,1,1,1,2,0,0,0,0}, + {2,1,1,1,1,1,1,1,2,0,0,0}, + {2,1,1,1,1,1,1,1,1,2,0,0}, + {2,1,1,1,1,1,2,2,2,2,2,0}, + {2,1,1,2,1,2,0,0,0,0,0,0}, + {2,1,2,0,0,2,0,0,0,0,0,0}, + {2,2,0,0,0,0,2,0,0,0,0,0}, + {2,0,0,0,0,0,2,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0}, +}; + +static layer_t* g_cursor_layer = NULL; + +void cursor_init(void) { + g_cursor_layer = layer_create("cursor", LAYER_TYPE_MOUSE, CURSOR_W, CURSOR_H); + if (!g_cursor_layer) return; + + layer_set_z(g_cursor_layer, 9999); + + // 将位图写入图层缓冲区,Reserved 用作 alpha(0=透明, 255=不透明) + EFI_GRAPHICS_OUTPUT_BLT_PIXEL* buf = g_cursor_layer->buffer; + for (UINT32 y = 0; y < CURSOR_H; y++) { + for (UINT32 x = 0; x < CURSOR_W; x++) { + EFI_GRAPHICS_OUTPUT_BLT_PIXEL* dst = &buf[y * CURSOR_W + x]; + SUINT8 px = g_cursor_bitmap[y][x]; + if (px == 1) { + dst->Blue = 255; dst->Green = 255; dst->Red = 255; dst->Reserved = 255; + } else if (px == 2) { + dst->Blue = 0; dst->Green = 0; dst->Red = 0; dst->Reserved = 255; + } else { + dst->Blue = 0; dst->Green = 0; dst->Red = 0; dst->Reserved = 0; + } + } + } + + // 初始位置:屏幕中心 + layer_set_pos(g_cursor_layer, + (SSINT32)(g_gfx.hr / 2), + (SSINT32)(g_gfx.vr / 2)); +} + +void cursor_update(void) { + if (!g_cursor_layer) return; + + SSINT32 cx = (SSINT32)(g_gfx.hr / 2) + mouse_get_x(); + SSINT32 cy = (SSINT32)(g_gfx.vr / 2) + mouse_get_y(); + + if (cx < 0) cx = 0; + if (cy < 0) cy = 0; + if (cx + (SSINT32)CURSOR_W > (SSINT32)g_gfx.hr) cx = (SSINT32)g_gfx.hr - CURSOR_W; + if (cy + (SSINT32)CURSOR_H > (SSINT32)g_gfx.vr) cy = (SSINT32)g_gfx.vr - CURSOR_H; + + layer_set_pos(g_cursor_layer, cx, cy); +} + +layer_t* cursor_get_layer(void) { + return g_cursor_layer; +} diff --git a/devices/mouse/mouse.cpp b/devices/mouse/mouse.cpp new file mode 100644 index 0000000..55ae2a0 --- /dev/null +++ b/devices/mouse/mouse.cpp @@ -0,0 +1,170 @@ +#include +#include +#include +#include + +static inline void outb(UINT16 port, UINT8 val) { + ASM("outb %0, %1" : : "a"(val), "Nd"(port)); +} + +static inline UINT8 inb(UINT16 port) { + UINT8 ret; + ASM("inb %1, %0" : "=a"(ret) : "Nd"(port)); + return ret; +} + +// 等待 PS/2 控制器输入缓冲区清空 +static void ps2_wait_input(void) { + for (int i = 0; i < 100000; i++) { + if (!(inb(PS2_STATUS_PORT) & PS2_STAT_IBF)) return; + } +} + +// 等待 PS/2 控制器输出缓冲区有数据 +static void ps2_wait_output(void) { + for (int i = 0; i < 100000; i++) { + if (inb(PS2_STATUS_PORT) & PS2_STAT_OBF) return; + } +} + +// 向 PS/2 控制器发送命令 +static void ps2_write_cmd(UINT8 cmd) { + ps2_wait_input(); + outb(PS2_CMD_PORT, cmd); +} + +// 读取 PS/2 控制器返回的数据 +static UINT8 ps2_read_data(void) { + ps2_wait_output(); + return inb(PS2_DATA_PORT); +} + +// 向辅助端口(鼠标)发送数据 +static void ps2_write_mouse(UINT8 data) { + ps2_write_cmd(0xD4); // Write to Auxiliary Device + ps2_wait_input(); + outb(PS2_DATA_PORT, data); +} + +// 鼠标 3 字节数据包状态 +static int g_pkt_phase = 0; // 0=byte0, 1=byte1, 2=byte2 +static UINT8 g_pkt_byte0 = 0; +static UINT8 g_pkt_byte1 = 0; + +// 鼠标光标位置(相对于屏幕中心) +static volatile SSINT32 g_mouse_x = 0; +static volatile SSINT32 g_mouse_y = 0; +static volatile bool g_mouse_btn_l = false; +static volatile bool g_mouse_btn_r = false; +static volatile bool g_mouse_btn_m = false; + +// IRQ12 处理函数 +static void mouse_irq_handler(trap_frame* frame) { + (void)frame; + pic_send_eoi(PS2_MOUSE_IRQ); + + // 读取状态寄存器,检查输出缓冲区是否有数据 + UINT8 status = inb(PS2_STATUS_PORT); + if (!(status & PS2_STAT_OBF)) return; + + UINT8 data = inb(PS2_DATA_PORT); + + // 解析 3 字节数据包 + switch (g_pkt_phase) { + case 0: // byte 0: 按钮 + 标志 + if (!(data & PS2_MOUSE_ALWAYS1)) return; // 无效字节,重新同步 + g_pkt_byte0 = data; + g_pkt_phase = 1; + break; + case 1: // byte 1: X delta + g_pkt_byte1 = data; + g_pkt_phase = 2; + break; + case 2: // byte 2: Y delta + g_pkt_byte0 = g_pkt_byte0; // 确保编译器不优化 + g_pkt_byte1 = g_pkt_byte1; + + // 解析按钮状态 + g_mouse_btn_l = (g_pkt_byte0 & PS2_MOUSE_LEFT) != 0; + g_mouse_btn_r = (g_pkt_byte0 & PS2_MOUSE_RIGHT) != 0; + g_mouse_btn_m = (g_pkt_byte0 & PS2_MOUSE_MIDDLE) != 0; + + // 解析 X delta + SSINT32 dx = (SSINT32)(SSINT8)g_pkt_byte1; + if (g_pkt_byte0 & PS2_MOUSE_X_SIGN) dx |= 0xFFFFFF00; + + // 解析 Y delta + SSINT32 dy = (SSINT32)(SSINT8)data; + if (g_pkt_byte0 & PS2_MOUSE_Y_SIGN) dy |= 0xFFFFFF00; + + // 更新光标位置(Y 轴在屏幕上向下为正) + g_mouse_x += dx; + g_mouse_y -= dy; + + g_pkt_phase = 0; + break; + } +} + +// 丢弃输出缓冲区中的残留数据 +static void ps2_flush_output(void) { + for (int i = 0; i < 100; i++) { + inb(PS2_DATA_PORT); + } +} + +void mouse_init(void) { + // 1. 控制器自检(必须在命令字节写入之前,因为自检会重置命令字节) + ps2_write_cmd(PS2_CMD_SELF_TEST); + UINT8 self_test = ps2_read_data(); + if (self_test != 0x55) { + serial_write("MOUSE: self-test FAILED\n"); + } + + // 2. 丢弃自检可能产生的残留数据 + ps2_flush_output(); + + // 3. 启用辅助端口(鼠标) + ps2_write_cmd(PS2_CMD_ENABLE_A2); + + // 4. 读取当前命令字节 + ps2_write_cmd(PS2_CMD_READ_MB); + UINT8 cmd = ps2_read_data(); + + // 5. 启用 IRQ12 + IRQ1 + cmd |= PS2_CMD_IRQ12 | PS2_CMD_IRQ1; + + // 6. 写入修改后的命令字节(在自检之后,不会被重置) + ps2_write_cmd(PS2_CMD_WRITE_MB); + ps2_wait_input(); + outb(PS2_DATA_PORT, cmd); + + // 7. 验证命令字节已写入 + ps2_write_cmd(PS2_CMD_READ_MB); + UINT8 verify = ps2_read_data(); + if (verify != cmd) { + serial_write("MOUSE: cmd verify mismatch\n"); + } + + // 重置鼠标 + ps2_write_mouse(PS2_MOUSE_RESET); + UINT8 ack = ps2_read_data(); + if (ack == 0xFA) { + ps2_read_data(); // 丢弃第二字节 + } + + // 启用鼠标数据上报 + ps2_write_mouse(PS2_MOUSE_ENABLE); + ps2_read_data(); + + // 注册 IRQ12 处理函数并取消屏蔽 + idt_set_handler(PS2_MOUSE_VECTOR, mouse_irq_handler); + pic_unmask_irq(2); // 取消主片级联屏蔽 + pic_unmask_irq(PS2_MOUSE_IRQ); +} + +SSINT32 mouse_get_x(void) { return g_mouse_x; } +SSINT32 mouse_get_y(void) { return g_mouse_y; } +bool mouse_get_btn_left(void) { return g_mouse_btn_l; } +bool mouse_get_btn_right(void) { return g_mouse_btn_r; } +bool mouse_get_btn_middle(void) { return g_mouse_btn_m; } diff --git a/include/devices/cursor.h b/include/devices/cursor.h new file mode 100644 index 0000000..b1c63cc --- /dev/null +++ b/include/devices/cursor.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +#define CURSOR_W 12 +#define CURSOR_H 16 + +void cursor_init(void); +void cursor_update(void); + +layer_t* cursor_get_layer(void); diff --git a/include/devices/mouse.h b/include/devices/mouse.h new file mode 100644 index 0000000..36d4454 --- /dev/null +++ b/include/devices/mouse.h @@ -0,0 +1,48 @@ +#pragma once + +#include + +#define PS2_MOUSE_IRQ 12 +#define PS2_MOUSE_VECTOR (PIC_IRQ_BASE + PS2_MOUSE_IRQ) + +// PS/2 controller ports +#define PS2_DATA_PORT 0x60 +#define PS2_STATUS_PORT 0x64 +#define PS2_CMD_PORT 0x64 + +// PS/2 controller commands +#define PS2_CMD_READ_MB 0x20 // Read command byte +#define PS2_CMD_WRITE_MB 0x60 // Write command byte +#define PS2_CMD_ENABLE_A2 0xA8 // Enable auxiliary (second) port +#define PS2_CMD_SELF_TEST 0xAA // Controller self-test + +// PS/2 command byte bits +#define PS2_CMD_IRQ12 0x02 // Enable IRQ12 (auxiliary device) +#define PS2_CMD_IRQ1 0x01 // Enable IRQ1 (keyboard) + +// PS/2 controller status bits +#define PS2_STAT_OBF 0x01 // Output buffer full (data ready) +#define PS2_STAT_IBF 0x02 // Input buffer full (controller busy) + +// PS/2 mouse commands +#define PS2_MOUSE_ENABLE 0xF4 // Enable data reporting +#define PS2_MOUSE_DISABLE 0xF5 // Disable data reporting +#define PS2_MOUSE_RESET 0xFF // Reset mouse + +// Mouse packet byte 0 flags +#define PS2_MOUSE_LEFT 0x01 +#define PS2_MOUSE_RIGHT 0x02 +#define PS2_MOUSE_MIDDLE 0x04 +#define PS2_MOUSE_ALWAYS1 0x08 +#define PS2_MOUSE_X_SIGN 0x10 +#define PS2_MOUSE_Y_SIGN 0x20 +#define PS2_MOUSE_X_OVER 0x40 +#define PS2_MOUSE_Y_OVER 0x80 + +void mouse_init(void); + +SSINT32 mouse_get_x(void); +SSINT32 mouse_get_y(void); +bool mouse_get_btn_left(void); +bool mouse_get_btn_right(void); +bool mouse_get_btn_middle(void); diff --git a/kernel/graphics/layer.cpp b/kernel/graphics/layer.cpp index dd193a0..1fe60bb 100644 --- a/kernel/graphics/layer.cpp +++ b/kernel/graphics/layer.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -102,12 +103,6 @@ layer_t* layer_create(const char* name, layer_type_t type, UINT32 w, UINT32 h) { layer_insert_sorted(layer); - serial_write("LAYER: created '"); - serial_write(name); - serial_write("' id="); - serial_write_hex(id); - serial_write("\n"); - return layer; } @@ -126,10 +121,6 @@ void layer_destroy(layer_t* 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) { @@ -234,16 +225,10 @@ void layer_init(void) { // 注册键盘 IRQ 并取消屏蔽 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"); } // 合成器任务 void layer_compositor_task(void) { - serial_write("LAYER: compositor task running\n"); - UINT32 hr = g_gfx.hr; UINT32 vr = g_gfx.vr; @@ -255,12 +240,12 @@ void layer_compositor_task(void) { 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"); } } + // 更新光标位置 + cursor_update(); + // 清除后台缓冲区 EFI_GRAPHICS_OUTPUT_BLT_PIXEL black = {0, 0, 0, 0}; draw_set_target(g_back_buffer, hr, vr); @@ -281,14 +266,17 @@ void layer_compositor_task(void) { if (dy + (SSINT32)sh > (SSINT32)vr) sh = vr - dy; if (sw == 0 || sh == 0) { cur = cur->next; continue; } + bool use_alpha = (cur->type == LAYER_TYPE_MOUSE); 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; + if (!use_alpha || src->Reserved != 0) { + dst->Blue = src->Blue; + dst->Green = src->Green; + dst->Red = src->Red; + dst->Reserved = src->Reserved; + } src++; dst++; } diff --git a/kernel/main.cpp b/kernel/main.cpp index cc31259..2b9c88c 100644 --- a/kernel/main.cpp +++ b/kernel/main.cpp @@ -15,6 +15,8 @@ #include #include #include +#include +#include extern EFI_SYSTEM_TABLE *ST; @@ -146,6 +148,10 @@ extern "C" void kernel_main() { // 注册 IRQ 处理函数(向量 0x20 = PIC_IRQ_BASE + 0) idt_set_handler(PIC_IRQ_BASE + 0, irq_handler); + // 初始化 PS/2 鼠标驱动 + serial_write("Sylva: init mouse...\n"); + mouse_init(); + serial_write("Sylva: init PIT...\n"); pit_init(); pit_set_tick_handler(scheduler_tick); @@ -160,6 +166,9 @@ extern "C" void kernel_main() { // 初始化合成器(分配后台缓冲区,注册键盘处理) layer_init(); + // 初始化鼠标光标 + cursor_init(); + // 创建桌面图层(全屏,z=0) layer_t* desktop = layer_create("desktop", LAYER_TYPE_DESKTOP, g_gfx.hr, g_gfx.vr); if (desktop) {