feat: 图形桌面 + 独立工具应用 + Sealed Arc UI 设计系统 | desktop.elf窗口管理器/6个工具应用(bash/editor/fileman/browser/curl/ping)/desktop_app.h公共头文件/Sealed Arc设计系统/DRR完整实现/Process增强/DSK启动改为加载desktop.elf/Shell重构/开发文档

This commit is contained in:
xyou
2026-07-03 21:13:04 +08:00
parent 3662345434
commit 48dcb5568e
44 changed files with 6302 additions and 220 deletions
+1438
View File
@@ -0,0 +1,1438 @@
/* Deshab Desktop Manager — 图形桌面环境
*
* 功能:窗口管理、任务栏、桌面图标、事件路由、应用加载
* 风格:Sealed Arc 视觉设计语言
* 输入:PS/2 键盘 + PS/2 鼠标
*/
#include "../UTSM/include/utsm/dsk.h"
/* block_read 函数类型(从 kernel_api + 0xA8 获取) */
typedef int (*desktop_block_read_fn)(void *ctx, unsigned long long lba, unsigned int count, void *buffer);
/* 先包含 ascii_bitmaps.c(定义 g_ascii),再包含 deshab_ui.h(引用 g_ascii) */
#include "../firstInit/ascii_bitmaps.c"
#include "../UTSM/include/utsm/deshab_ui.h"
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef long long i64;
#define COM1 0x3F8
static __inline__ void outb(u16 port, u8 value) { __asm__ volatile("outb %0,%1"::"a"(value),"Nd"(port)); }
static __inline__ u8 inb(u16 port) { u8 v; __asm__ volatile("inb %1,%0":"=a"(v):"Nd"(port)); return v; }
static void sputc(char c) {
for (unsigned int i=0; i<100000; i++) { if (inb(COM1+5)&0x20) break; }
outb(COM1, (unsigned char)c);
}
static void swrite(const char *s) { while(*s) { if(*s=='\n')sputc('\r'); sputc(*s++); } }
static void slog(const char *s) { swrite("[desktop] "); swrite(s); swrite("\n"); }
/* ============================================================
* 常量
* ============================================================ */
#define MAX_WINDOWS 8
#define TASKBAR_H 40
#define TITLEBAR_H 28
#define BORDER_W 2
#define DESKTOP_ICON_W 80
#define DESKTOP_ICON_H 72
#define ICON_GUTTER_X 16
#define ICON_GUTTER_Y 16
#define CURSOR_SIZE 24
/* ============================================================
* 事件系统
* ============================================================ */
typedef enum {
EV_MOUSE_MOVE = 1,
EV_MOUSE_DOWN,
EV_MOUSE_UP,
EV_KEY_DOWN,
EV_KEY_UP,
} ev_type;
typedef struct {
ev_type type;
int mx, my; /* 鼠标全局坐标 */
int button; /* 0=左, 1=右 */
u8 scancode;
int shift, ctrl, alt;
} desktop_event;
/* ============================================================
* 窗口
* ============================================================ */
typedef struct desktop_window {
int id;
int x, y, w, h;
int visible;
int focused;
int minimized;
char title[48];
int app_id; /* 关联应用 ID */
void *app_state; /* 应用私有状态 */
int closing; /* 关闭中标记 */
int dirty; /* 需要重绘 */
} desktop_window;
/* ============================================================
* 应用描述符
* ============================================================ */
/* 前向声明 */
struct desktop_window;
typedef struct desktop_window desktop_win;
typedef struct app_ctx {
du_context *fb;
desktop_win *win;
int client_x, client_y, client_w, client_h;
int should_exit;
desktop_block_read_fn block_read;
const void *kernel_api;
} app_ctx;
typedef void *(*app_create_fn)(app_ctx *ctx);
typedef void (*app_event_fn)(void *state, app_ctx *ctx, desktop_event *ev);
typedef void (*app_draw_fn)(void *state, app_ctx *ctx);
typedef void (*app_destroy_fn)(void *state);
typedef struct {
const char *name;
const char *display_name;
u32 default_w, default_h;
app_create_fn on_create;
app_event_fn on_event;
app_draw_fn on_draw;
app_destroy_fn on_destroy;
} app_descriptor;
/* ============================================================
* 全局状态
* ============================================================ */
static du_context g_fb;
static u64 g_fb_addr, g_fb_w, g_fb_h, g_fb_pitch;
static const void *g_kernel_api;
static desktop_block_read_fn g_block_read;
/* 鼠标状态 */
static int g_mouse_x = 400, g_mouse_y = 300;
static int g_mouse_btn = 0; /* bit0=左键 */
static u8 g_mouse_buf[3];
static int g_mouse_idx = 0;
static int g_mouse_has_pkt = 0;
/* 光标背景保存 */
static u32 g_cursor_save[CURSOR_SIZE * CURSOR_SIZE];
static int g_cursor_saved = 0;
static int g_cursor_old_x = -1, g_cursor_old_y = -1;
/* 键盘状态 */
static int g_shift = 0, g_ctrl = 0, g_alt = 0;
static int g_e0 = 0;
/* 窗口列表 */
static desktop_window g_windows[MAX_WINDOWS];
static int g_win_count = 0;
static int g_next_win_id = 1;
static int g_focused_win = -1;
/* 拖拽状态 */
static int g_dragging = 0;
static int g_drag_win = -1;
static int g_drag_off_x, g_drag_off_y;
/* 应用表 */
#define MAX_APPS 8
static app_descriptor g_apps[MAX_APPS];
static int g_app_count = 0;
/* 桌面图标 */
typedef struct {
int app_id;
int x, y; /* 图标位置 */
const char *label;
} desktop_icon;
#define MAX_ICONS 8
static desktop_icon g_icons[MAX_ICONS];
static int g_icon_count = 0;
/* ============================================================
* 箭头光标形状(24×24,1=前景 0=透明)
* ============================================================ */
static const u8 cursor_shape[24][24] = {
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
};
/* ============================================================
* 应用图标形状(32×32 像素数据,用程序化方式生成)
* ============================================================ */
/* 绘制终端图标(32×32)到指定位置 */
static void draw_icon_terminal(du_context *ctx, int cx, int cy) {
u32 bg = DP_ABYSS_700;
u32 fg = DP_SEAL_300;
u32 border = DP_SEAL_500;
/* 圆角矩形背景 */
du_fill_rounded_rect(ctx, cx, cy, 32, 32, bg, 6);
du_rect_outline(ctx, cx, cy, 32, 32, border, 6);
/* >_ 符号 */
du_fill_rect(ctx, cx+5, cy+10, 8, 2, fg);
du_fill_rect(ctx, cx+5, cy+10, 2, 6, fg);
du_fill_rect(ctx, cx+13, cy+12, 2, 2, fg);
du_fill_rect(ctx, cx+17, cy+16, 6, 2, fg);
}
/* 绘制编辑器图标(32×32) */
static void draw_icon_editor(du_context *ctx, int cx, int cy) {
u32 bg = DP_ABYSS_700;
u32 fg = DP_SEAL_500;
u32 border = DP_SEAL_300;
du_fill_rounded_rect(ctx, cx, cy, 32, 32, bg, 6);
du_rect_outline(ctx, cx, cy, 32, 32, border, 6);
/* 文本行 */
for (int i = 0; i < 5; i++) {
int lw = (i == 0) ? 20 : (i == 3 ? 12 : 16);
du_fill_rect(ctx, cx+6, cy+6+i*5, lw, 2, fg);
}
}
/* 绘制文件夹图标(32×32) */
static void draw_icon_folder(du_context *ctx, int cx, int cy) {
u32 bg = DP_ARC_500;
u32 fg = DP_ARC_300;
u32 border = DP_ARC_700;
du_fill_rounded_rect(ctx, cx, cy, 32, 32, bg, 6);
du_rect_outline(ctx, cx, cy, 32, 32, border, 6);
/* 文件夹标签 */
du_fill_rect(ctx, cx+4, cy+6, 12, 3, fg);
/* 文件夹主体 */
du_fill_rect(ctx, cx+4, cy+12, 24, 2, border);
du_fill_rect(ctx, cx+4, cy+14, 24, 12, fg);
}
/* 绘制计算器图标(32×32) */
static void draw_icon_calc(du_context *ctx, int cx, int cy) {
u32 bg = DP_ABYSS_700;
u32 fg = DP_WARNING;
u32 border = DP_WARNING;
du_fill_rounded_rect(ctx, cx, cy, 32, 32, bg, 6);
du_rect_outline(ctx, cx, cy, 32, 32, border, 6);
/* 屏幕 */
du_fill_rect(ctx, cx+4, cy+4, 24, 6, DP_ABYSS_900);
du_fill_rect(ctx, cx+22, cy+5, 4, 4, fg);
/* 按键网格 */
for (int r = 0; r < 3; r++) {
for (int c = 0; c < 3; c++) {
du_fill_rect(ctx, cx+4+c*8, cy+14+r*6, 6, 4, DP_NEUTRAL_400);
}
}
}
/* ============================================================
* 光标管理
* ============================================================ */
static void cursor_save_bg(int mx, int my) {
for (int r = 0; r < CURSOR_SIZE; r++) {
for (int c = 0; c < CURSOR_SIZE; c++) {
int x = mx + c, y = my + r;
if (x >= 0 && (u64)x < g_fb_w && y >= 0 && (u64)y < g_fb_h) {
u32 *line = (u32 *)((u8 *)g_fb.fb + (u64)y * g_fb.pitch);
g_cursor_save[r * CURSOR_SIZE + c] = line[x];
} else {
g_cursor_save[r * CURSOR_SIZE + c] = 0;
}
}
}
g_cursor_saved = 1;
g_cursor_old_x = mx;
g_cursor_old_y = my;
}
static void cursor_restore_bg(void) {
if (!g_cursor_saved) return;
for (int r = 0; r < CURSOR_SIZE; r++) {
for (int c = 0; c < CURSOR_SIZE; c++) {
int x = g_cursor_old_x + c, y = g_cursor_old_y + r;
if (x >= 0 && (u64)x < g_fb_w && y >= 0 && (u64)y < g_fb_h) {
u32 *line = (u32 *)((u8 *)g_fb.fb + (u64)y * g_fb.pitch);
line[x] = g_cursor_save[r * CURSOR_SIZE + c];
}
}
}
g_cursor_saved = 0;
}
static void cursor_draw(int mx, int my) {
for (int r = 0; r < CURSOR_SIZE; r++) {
for (int c = 0; c < CURSOR_SIZE; c++) {
if (cursor_shape[r][c]) {
du_pixel(&g_fb, mx + c, my + r, DS_DARK_TEXT_PRIMARY);
}
}
}
}
/* ============================================================
* 窗口管理
* ============================================================ */
static desktop_window *win_find(int id) {
for (int i = 0; i < g_win_count; i++) {
if (g_windows[i].id == id) return &g_windows[i];
}
return 0;
}
static desktop_window *win_find_at(int mx, int my) {
/* 从后往前搜索(后面的窗口在上面) */
for (int i = g_win_count - 1; i >= 0; i--) {
desktop_window *w = &g_windows[i];
if (!w->visible || w->minimized) continue;
if (mx >= w->x && mx < w->x + w->w && my >= w->y && my < w->y + w->h) {
return w;
}
}
return 0;
}
static void win_bring_to_front(desktop_window *w) {
int idx = -1;
for (int i = 0; i < g_win_count; i++) {
if (g_windows[i].id == w->id) { idx = i; break; }
}
if (idx < 0 || idx == g_win_count - 1) return;
desktop_window tmp = g_windows[idx];
for (int i = idx; i < g_win_count - 1; i++) {
g_windows[i] = g_windows[i+1];
}
g_windows[g_win_count - 1] = tmp;
}
static desktop_window *win_create(int app_id, const char *title, int x, int y, int w, int h) {
if (g_win_count >= MAX_WINDOWS) return 0;
desktop_window *win = &g_windows[g_win_count++];
int id = g_next_win_id++;
win->id = id;
win->x = x; win->y = y; win->w = w; win->h = h;
win->visible = 1;
win->focused = 1;
win->minimized = 0;
win->app_id = app_id;
win->app_state = 0;
win->closing = 0;
win->dirty = 1;
/* 拷贝标题 */
for (int i = 0; i < 47 && title[i]; i++) win->title[i] = title[i];
win->title[47] = 0;
/* 取消其他窗口焦点 */
for (int i = 0; i < g_win_count - 1; i++) g_windows[i].focused = 0;
g_focused_win = id;
return win;
}
static void win_destroy(desktop_window *w) {
int idx = -1;
for (int i = 0; i < g_win_count; i++) {
if (g_windows[i].id == w->id) { idx = i; break; }
}
if (idx < 0) return;
/* 调用应用的 on_destroy */
if (w->app_id >= 0 && w->app_id < g_app_count && g_apps[w->app_id].on_destroy && w->app_state) {
g_apps[w->app_id].on_destroy(w->app_state);
}
for (int i = idx; i < g_win_count - 1; i++) g_windows[i] = g_windows[i+1];
g_win_count--;
if (g_focused_win == w->id) {
g_focused_win = g_win_count > 0 ? g_windows[g_win_count-1].id : -1;
if (g_focused_win >= 0) win_find(g_focused_win)->focused = 1;
}
}
static int win_hit_titlebar(desktop_window *w, int mx, int my) {
return mx >= w->x && mx < w->x + w->w &&
my >= w->y && my < w->y + TITLEBAR_H;
}
static int win_hit_close_btn(desktop_window *w, int mx, int my) {
int bx = w->x + w->w - 28;
int by = w->y + 4;
return mx >= bx && mx < bx + 24 && my >= by && my < by + 20;
}
/* ============================================================
* 窗口装饰绘制
* ============================================================ */
static void draw_window_frame(desktop_window *w) {
if (!w->visible || w->minimized) return;
u32 titlebar_bg = w->focused ? DP_ABYSS_600 : DP_ABYSS_800;
u32 border_col = w->focused ? DS_DARK_BORDER_FOCUS : DS_DARK_BORDER;
/* 发光效果(焦点窗口) */
if (w->focused) {
du_rect_glow(&g_fb, w->x-2, w->y-2, w->w+4, w->h+4,
DS_DARK_BORDER_FOCUS, 10, 3);
}
/* 窗口主体背景 */
du_fill_rounded_rect(&g_fb, w->x, w->y, w->w, w->h,
DS_DARK_BG_SECONDARY, DU_RADIUS_LG);
/* 标题栏 */
du_fill_rect(&g_fb, w->x + BORDER_W, w->y + BORDER_W,
w->w - 2*BORDER_W, TITLEBAR_H - BORDER_W, titlebar_bg);
/* 标题文字 */
du_draw_string(&g_fb, w->title,
w->x + (i64)DU_SPACE_SM,
w->y + (TITLEBAR_H - (i64)DU_ASCII_CELL_H) / 2,
DS_DARK_TEXT_PRIMARY, titlebar_bg, DU_ASCII_STEP);
/* 关闭按钮 */
int bx = w->x + w->w - 28;
int by = w->y + 4;
du_fill_rounded_rect(&g_fb, bx, by, 24, 20, DP_ERROR, DU_RADIUS_SM);
du_draw_string(&g_fb, "X", bx + 8, by + 1, DP_NEUTRAL_900, DP_ERROR, DU_ASCII_STEP);
/* 边框 */
du_rect_outline(&g_fb, w->x, w->y, w->w, w->h, border_col, DU_RADIUS_LG);
/* 标题栏底线 */
du_fill_rect(&g_fb, w->x + BORDER_W, w->y + TITLEBAR_H,
w->w - 2*BORDER_W, 1, border_col);
}
/* ============================================================
* 任务栏绘制
* ============================================================ */
static void draw_taskbar(void) {
i64 tb_y = (i64)g_fb_h - TASKBAR_H;
du_fill_rect(&g_fb, 0, tb_y, (i64)g_fb_w, TASKBAR_H, DP_ABYSS_800);
/* 顶部强调线 */
du_fill_rect(&g_fb, 0, tb_y, (i64)g_fb_w, 2, DS_DARK_ACCENT);
/* Start 按钮 */
du_fill_rounded_rect(&g_fb, (i64)DU_SPACE_SM, tb_y + 4, 48, 32,
DP_ABYSS_600, DU_RADIUS_SM);
du_draw_string(&g_fb, "D",
(i64)DU_SPACE_SM + 18, tb_y + 8,
DS_DARK_ACCENT_LIGHT, DP_ABYSS_600, DU_ASCII_STEP);
/* 快速启动图标 */
int qx = 64;
for (int i = 0; i < g_app_count && i < 4; i++) {
int icon_x = qx + i * 40;
switch (i) {
case 0: draw_icon_terminal(&g_fb, icon_x + 4, (int)tb_y + 4); break;
case 1: draw_icon_editor(&g_fb, icon_x + 4, (int)tb_y + 4); break;
case 2: draw_icon_folder(&g_fb, icon_x + 4, (int)tb_y + 4); break;
case 3: draw_icon_calc(&g_fb, icon_x + 4, (int)tb_y + 4); break;
}
}
/* 窗口按钮 */
int wx = qx + 4 * 40 + (int)DU_SPACE_LG;
for (int i = 0; i < g_win_count; i++) {
desktop_window *w = &g_windows[i];
if (w->minimized) continue;
u32 btn_bg = w->focused ? DS_DARK_ACCENT : DP_ABYSS_600;
du_fill_rounded_rect(&g_fb, wx, tb_y + 4, 100, 32, btn_bg, DU_RADIUS_SM);
du_draw_string(&g_fb, w->title, wx + 4, tb_y + 8,
DS_DARK_TEXT_PRIMARY, btn_bg, DU_ASCII_STEP);
wx += 104;
if (wx > (int)g_fb_w - 200) break;
}
/* 时钟 */
u8 rtc_h = 0, rtc_m = 0;
outb(0x70, 4); rtc_h = inb(0x71);
outb(0x70, 2); rtc_m = inb(0x71);
/* BCD → 二进制 */
rtc_h = (u8)((rtc_h >> 4) * 10 + (rtc_h & 0xF));
rtc_m = (u8)((rtc_m >> 4) * 10 + (rtc_m & 0xF));
char clock_str[8];
clock_str[0] = '0' + rtc_h / 10;
clock_str[1] = '0' + rtc_h % 10;
clock_str[2] = ':';
clock_str[3] = '0' + rtc_m / 10;
clock_str[4] = '0' + rtc_m % 10;
clock_str[5] = 0;
du_draw_string(&g_fb, clock_str,
(i64)g_fb_w - 5 * (i64)DU_ASCII_STEP - (i64)DU_SPACE_MD,
tb_y + (TASKBAR_H - (i64)DU_ASCII_CELL_H) / 2,
DS_DARK_TEXT_SECONDARY, DP_ABYSS_800, DU_ASCII_STEP);
}
/* ============================================================
* 桌面图标绘制
* ============================================================ */
static void draw_desktop_icons(void) {
for (int i = 0; i < g_icon_count; i++) {
desktop_icon *ic = &g_icons[i];
int cx = ic->x;
int cy = ic->y;
/* 根据应用 ID 画不同图标 */
switch (ic->app_id) {
case 0: draw_icon_terminal(&g_fb, cx, cy); break;
case 1: draw_icon_editor(&g_fb, cx, cy); break;
case 2: draw_icon_folder(&g_fb, cx, cy); break;
case 3: draw_icon_calc(&g_fb, cx, cy); break;
default: draw_icon_terminal(&g_fb, cx, cy); break;
}
/* 标签文字 */
int label_len = 0;
while (ic->label[label_len]) label_len++;
int label_x = cx + (32 - label_len * (int)DU_ASCII_STEP) / 2;
if (label_x < cx) label_x = cx;
du_draw_string(&g_fb, ic->label, label_x, cy + 36,
DS_DARK_TEXT_PRIMARY, DS_DARK_BG_PRIMARY, DU_ASCII_STEP);
}
}
/* ============================================================
* PS/2 鼠标处理
* ============================================================ */
static void ps2_mouse_init(void) {
/* 启用 AUX 设备 */
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
outb(0x64, 0xA8);
/* 启用 AUX IRQ */
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
outb(0x64, 0x20);
for (int t = 0; t < 100000; t++) { if (inb(0x64) & 1) break; }
u8 cfg = inb(0x60);
cfg |= 0x02; /* enable AUX IRQ12 */
cfg &= ~0x20; /* enable AUX clock */
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
outb(0x64, 0x60);
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
outb(0x60, cfg);
/* 复位鼠标 */
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
outb(0x64, 0xD4);
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
outb(0x60, 0xFF);
for (int t = 0; t < 100000; t++) { if (inb(0x64) & 1) break; }
inb(0x60); /* ACK */
/* 启用 streaming */
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
outb(0x64, 0xD4);
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
outb(0x60, 0xF4);
for (int t = 0; t < 100000; t++) { if (inb(0x64) & 1) break; }
inb(0x60); /* ACK */
slog("mouse init ok");
}
static int ps2_mouse_poll(void) {
u8 st = inb(0x64);
if (!(st & 1)) return 0;
u8 data = inb(0x60);
if (!(st & 0x20)) return 0; /* 不是鼠标数据 */
g_mouse_buf[g_mouse_idx++] = data;
if (g_mouse_idx < 3) return 0;
g_mouse_idx = 0;
/* 检查 bit3 同步 */
if (!(g_mouse_buf[0] & 0x08)) return 0;
int dx = (int)(i8)g_mouse_buf[1];
int dy = (int)(i8)g_mouse_buf[2];
if (g_mouse_buf[0] & 0x10) dx = -dx; /* X sign */
/* 注意:Y 轴方向(鼠标 Y 向上为正,屏幕 Y 向下为正) */
if (!(g_mouse_buf[0] & 0x20)) dy = -dy; /* Y sign */
else dy = -dy;
dy = -dy; /* 鼠标 Y 翻转 */
g_mouse_x += dx;
g_mouse_y += dy;
if (g_mouse_x < 0) g_mouse_x = 0;
if (g_mouse_y < 0) g_mouse_y = 0;
if (g_mouse_x >= (int)g_fb_w - CURSOR_SIZE) g_mouse_x = (int)g_fb_w - CURSOR_SIZE;
if (g_mouse_y >= (int)g_fb_h - CURSOR_SIZE) g_mouse_y = (int)g_fb_h - CURSOR_SIZE;
g_mouse_btn = (g_mouse_buf[0] & 0x01) ? 1 : 0;
g_mouse_has_pkt = 1;
return 1;
}
/* ============================================================
* 键盘扫描码 → ASCII
* ============================================================ */
static char scan_to_ascii(u8 sc, int shift) {
static const char normal[58] = {
0, 27, '1','2','3','4','5','6','7','8','9','0','-','=', 8, '\t',
'q','w','e','r','t','y','u','i','o','p','[',']','\n',0,
'a','s','d','f','g','h','j','k','l',';','\'', '`',0,'\\',
'z','x','c','v','b','n','m',',','.','/',0,'*',0,' '
};
static const char shifted[58] = {
0, 27, '!','@','#','$','%','^','&','*','(',')','_','+', 8, '\t',
'Q','W','E','R','T','Y','U','I','O','P','{','}','\n',0,
'A','S','D','F','G','H','J','K','L',':','"','~',0,'|',
'Z','X','C','V','B','N','M','<','>','?',0,'*',0,' '
};
if (sc >= 58) return 0;
return shift ? shifted[sc] : normal[sc];
}
/* ============================================================
* 内置应用:终端 (Bash)
* ============================================================ */
#define TERM_MAX_COLS 80
#define TERM_MAX_ROWS 40
#define TERM_MAX_CHARS (TERM_MAX_COLS * TERM_MAX_ROWS)
typedef struct {
u8 ch[TERM_MAX_CHARS];
u32 fg[TERM_MAX_CHARS];
int cols, rows;
int cur_col, cur_row;
char input_buf[256];
int input_len, input_cursor;
int prompt_len;
} bash_state;
static const char *BASH_PROMPT = "deshab# ";
static void *bash_on_create(app_ctx *ctx) {
bash_state *s = (bash_state *)0x4000000; /* 固定地址分配,避免堆依赖 */
for (int i = 0; i < TERM_MAX_CHARS; i++) {
s->ch[i] = ' '; s->fg[i] = DS_DARK_TEXT_PRIMARY;
}
s->cols = (ctx->client_w - (int)DU_SPACE_SM * 2) / (int)DU_ASCII_STEP;
s->rows = (ctx->client_h - (int)DU_SPACE_SM * 2) / (int)DU_ASCII_LINE_H;
if (s->cols > TERM_MAX_COLS) s->cols = TERM_MAX_COLS;
if (s->rows > TERM_MAX_ROWS) s->rows = TERM_MAX_ROWS;
s->cur_col = 0; s->cur_row = 0;
s->input_len = 0; s->input_cursor = 0;
s->prompt_len = 0;
while (BASH_PROMPT[s->prompt_len]) s->prompt_len++;
/* 打印欢迎 */
const char *welcome = "Deshab Bash v0.1\n输入 help 查看命令\n\n";
while (*welcome) {
if (*welcome == '\n') {
s->cur_col = 0; s->cur_row++;
} else {
if (s->cur_row < s->rows && s->cur_col < s->cols) {
int idx = s->cur_row * TERM_MAX_COLS + s->cur_col;
s->ch[idx] = (u8)*welcome;
s->fg[idx] = DS_DARK_TEXT_PRIMARY;
}
s->cur_col++;
}
welcome++;
}
return s;
}
static void bash_putc(bash_state *s, char c, u32 color) {
if (c == '\n') { s->cur_col = 0; s->cur_row++; return; }
if (s->cur_row >= s->rows) {
/* 滚动 */
for (int r = 1; r < s->rows; r++) {
for (int c2 = 0; c2 < s->cols; c2++) {
s->ch[(r-1)*TERM_MAX_COLS+c2] = s->ch[r*TERM_MAX_COLS+c2];
s->fg[(r-1)*TERM_MAX_COLS+c2] = s->fg[r*TERM_MAX_COLS+c2];
}
}
for (int c2 = 0; c2 < s->cols; c2++) {
s->ch[(s->rows-1)*TERM_MAX_COLS+c2] = ' ';
}
s->cur_row = s->rows - 1;
}
if (s->cur_col >= s->cols) { s->cur_col = 0; s->cur_row++; }
int idx = s->cur_row * TERM_MAX_COLS + s->cur_col;
s->ch[idx] = (u8)c; s->fg[idx] = color;
s->cur_col++;
}
static void bash_puts(bash_state *s, const char *str, u32 color) {
while (*str) bash_putc(s, *str++, color);
}
static void bash_execute(bash_state *s, const char *cmd) {
while (*cmd == ' ') cmd++;
bash_puts(s, BASH_PROMPT, DS_DARK_PROMPT);
bash_puts(s, cmd, DS_DARK_TEXT_PRIMARY);
bash_putc(s, '\n', 0);
if (*cmd == 0) return;
if (cmd[0]=='h'&&cmd[1]=='e'&&cmd[2]=='l'&&cmd[3]=='p') {
bash_puts(s, " help clear echo version\n", DP_SUCCESS);
bash_puts(s, " uname date about reboot\n", DP_SUCCESS);
bash_puts(s, " halt pwd whoami id\n", DP_SUCCESS);
} else if (cmd[0]=='c'&&cmd[1]=='l'&&cmd[2]=='e'&&cmd[3]=='a'&&cmd[4]=='r') {
for (int i = 0; i < TERM_MAX_CHARS; i++) { s->ch[i] = ' '; }
s->cur_col = 0; s->cur_row = 0;
} else if (cmd[0]=='v'&&cmd[1]=='e'&&cmd[2]=='r') {
bash_puts(s, "Deshab OS v0.1.0\n", DP_SUCCESS);
} else if (cmd[0]=='u'&&cmd[1]=='n'&&cmd[2]=='a') {
bash_puts(s, "Deshab\n", DS_DARK_TEXT_PRIMARY);
} else if (cmd[0]=='a'&&cmd[1]=='b'&&cmd[2]=='o'&&cmd[3]=='u'&&cmd[4]=='t') {
bash_puts(s, "Deshab OS - SAS-R0 Kernel\n", DP_SEAL_300);
bash_puts(s, "Sealed Arc UI Design\n", DP_ARC_300);
} else if (cmd[0]=='e'&&cmd[1]=='c'&&cmd[2]=='h'&&cmd[3]=='o') {
const char *arg = cmd + 4;
while (*arg == ' ') arg++;
bash_puts(s, arg, DS_DARK_TEXT_PRIMARY);
bash_putc(s, '\n', 0);
} else if (cmd[0]=='p'&&cmd[1]=='w'&&cmd[2]=='d') {
bash_puts(s, "/\n", DS_DARK_TEXT_PRIMARY);
} else if (cmd[0]=='w'&&cmd[1]=='h'&&cmd[2]=='o') {
bash_puts(s, "root\n", DS_DARK_TEXT_PRIMARY);
} else if (cmd[0]=='i'&&cmd[1]=='d') {
bash_puts(s, "uid=0(root) gid=0(root)\n", DS_DARK_TEXT_PRIMARY);
} else if (cmd[0]=='r'&&cmd[1]=='e'&&cmd[2]=='b') {
bash_puts(s, "Rebooting...\n", DS_DARK_PROMPT);
outb(0x64, 0xFE);
for(;;) __asm__("hlt");
} else if (cmd[0]=='h'&&cmd[1]=='a'&&cmd[2]=='l'&&cmd[3]=='t') {
bash_puts(s, "Halted.\n", DS_DARK_PROMPT);
for(;;) __asm__("hlt");
} else {
bash_puts(s, "unknown: ", DP_ERROR);
bash_puts(s, cmd, DP_ERROR);
bash_putc(s, '\n', 0);
}
}
static void bash_on_event(void *state, app_ctx *ctx, desktop_event *ev) {
bash_state *s = (bash_state *)state;
if (ev->type != EV_KEY_DOWN) return;
u8 sc = ev->scancode;
if (sc == 0x1C) { /* Enter */
s->input_buf[s->input_len] = 0;
bash_execute(s, s->input_buf);
s->input_len = 0; s->input_cursor = 0;
} else if (sc == 0x0E) { /* Backspace */
if (s->input_cursor > 0) {
for (int i = s->input_cursor-1; i < s->input_len-1; i++)
s->input_buf[i] = s->input_buf[i+1];
s->input_len--; s->input_cursor--;
}
} else {
char c = scan_to_ascii(sc, ev->shift);
if (c && c >= 32 && c <= 126 && s->input_len < 255) {
for (int i = s->input_len; i > s->input_cursor; i--)
s->input_buf[i] = s->input_buf[i-1];
s->input_buf[s->input_cursor++] = c;
s->input_len++;
}
}
ctx->win->dirty = 1;
}
static void bash_on_draw(void *state, app_ctx *ctx) {
bash_state *s = (bash_state *)state;
int cx = ctx->client_x;
int cy = ctx->client_y;
int cw = ctx->client_w;
int ch = ctx->client_h;
/* 背景 */
du_fill_rect(&g_fb, cx, cy, cw, ch, DS_DARK_BG_PRIMARY);
/* 渲染所有字符 */
for (int r = 0; r < s->rows; r++) {
for (int c = 0; c < s->cols; c++) {
int idx = r * TERM_MAX_COLS + c;
if (s->ch[idx] == ' ') continue;
du_draw_char(&g_fb, s->ch[idx],
cx + (int)DU_SPACE_SM + c * (int)DU_ASCII_STEP,
cy + (int)DU_SPACE_SM + r * (int)DU_ASCII_LINE_H,
s->fg[idx], DS_DARK_BG_PRIMARY);
}
}
/* 绘制当前输入行 */
int input_y = cy + (int)DU_SPACE_SM + s->cur_row * (int)DU_ASCII_LINE_H;
/* 提示符 */
du_draw_string(&g_fb, BASH_PROMPT,
cx + (int)DU_SPACE_SM, input_y,
DS_DARK_PROMPT, DS_DARK_BG_PRIMARY, DU_ASCII_STEP);
/* 输入内容 */
du_draw_string(&g_fb, s->input_buf,
cx + (int)DU_SPACE_SM + s->prompt_len * (int)DU_ASCII_STEP,
input_y,
DS_DARK_TEXT_PRIMARY, DS_DARK_BG_PRIMARY, DU_ASCII_STEP);
/* 光标 */
int cursor_x = cx + (int)DU_SPACE_SM + (s->prompt_len + s->input_cursor) * (int)DU_ASCII_STEP;
du_fill_rect(&g_fb, cursor_x, input_y + (int)DU_ASCII_CELL_H - 3,
(int)DU_ASCII_CELL_W, 2, DS_DARK_CURSOR);
}
static void bash_on_destroy(void *state) {
(void)state;
}
/* ============================================================
* 内置应用:文本编辑器 (Editor)
* ============================================================ */
#define EDITOR_BUF_SIZE 65536
typedef struct {
char text[EDITOR_BUF_SIZE];
int text_len;
int cursor_pos;
int scroll_y; /* 行偏移 */
int cursor_line;
int cursor_col;
int modified;
char filename[64];
} editor_state;
static void editor_recalc_cursor(editor_state *s) {
s->cursor_line = 0; s->cursor_col = 0;
for (int i = 0; i < s->cursor_pos && i < s->text_len; i++) {
if (s->text[i] == '\n') { s->cursor_line++; s->cursor_col = 0; }
else s->cursor_col++;
}
}
static int editor_line_start(editor_state *s, int line) {
int l = 0;
for (int i = 0; i < s->text_len; i++) {
if (l == line) return i;
if (s->text[i] == '\n') l++;
}
return s->text_len;
}
static void *editor_on_create(app_ctx *ctx) {
editor_state *s = (editor_state *)0x5000000; /* 固定地址 */
s->text_len = 0; s->cursor_pos = 0;
s->scroll_y = 0; s->modified = 0;
s->filename[0] = 0;
/* 默认内容 */
const char *sample = "# Welcome to Deshab Editor\n#\n\nStart typing here...\n";
while (*sample && s->text_len < EDITOR_BUF_SIZE - 1) {
s->text[s->text_len++] = *sample++;
}
s->cursor_pos = s->text_len;
editor_recalc_cursor(s);
/* 设置窗口标题 */
du_draw_string(&g_fb, "", 0, 0, 0, 0, 0); /* 占位 */
for (int i = 0; i < 63; i++) ctx->win->title[i] = 0;
const char *t = "Editor - untitled";
for (int i = 0; t[i] && i < 47; i++) ctx->win->title[i] = t[i];
(void)ctx;
return s;
}
static void editor_on_event(void *state, app_ctx *ctx, desktop_event *ev) {
editor_state *s = (editor_state *)state;
if (ev->type != EV_KEY_DOWN) return;
u8 sc = ev->scancode;
int e0 = 0; /* TODO: 扩展键处理 */
if (sc == 0x1C) { /* Enter */
if (s->text_len < EDITOR_BUF_SIZE - 1) {
for (int i = s->text_len; i > s->cursor_pos; i--)
s->text[i] = s->text[i-1];
s->text[s->cursor_pos] = '\n';
s->text_len++; s->cursor_pos++;
s->modified = 1;
}
} else if (sc == 0x0E) { /* Backspace */
if (s->cursor_pos > 0) {
for (int i = s->cursor_pos - 1; i < s->text_len - 1; i++)
s->text[i] = s->text[i+1];
s->text_len--; s->cursor_pos--;
s->modified = 1;
}
} else if (sc == 0x48) { /* Up */
if (s->cursor_line > 0) {
int prev_start = editor_line_start(s, s->cursor_line - 1);
int col = s->cursor_col;
s->cursor_pos = prev_start;
for (int i = 0; i < col && s->cursor_pos < s->text_len && s->text[s->cursor_pos] != '\n'; i++)
s->cursor_pos++;
}
} else if (sc == 0x50) { /* Down */
if (s->cursor_line < 999) {
int next_start = editor_line_start(s, s->cursor_line + 1);
int col = s->cursor_col;
s->cursor_pos = next_start;
for (int i = 0; i < col && s->cursor_pos < s->text_len && s->text[s->cursor_pos] != '\n'; i++)
s->cursor_pos++;
}
} else if (sc == 0x4B) { /* Left */
if (s->cursor_pos > 0) s->cursor_pos--;
} else if (sc == 0x4D) { /* Right */
if (s->cursor_pos < s->text_len) s->cursor_pos++;
} else if (sc == 0x47) { /* Home */
s->cursor_pos = editor_line_start(s, s->cursor_line);
} else if (sc == 0x4F) { /* End */
s->cursor_pos = editor_line_start(s, s->cursor_line);
while (s->cursor_pos < s->text_len && s->text[s->cursor_pos] != '\n')
s->cursor_pos++;
} else {
char c = scan_to_ascii(sc, ev->shift);
if (c && c >= 32 && c <= 126 && s->text_len < EDITOR_BUF_SIZE - 1) {
for (int i = s->text_len; i > s->cursor_pos; i--)
s->text[i] = s->text[i-1];
s->text[s->cursor_pos] = c;
s->text_len++; s->cursor_pos++;
s->modified = 1;
}
}
editor_recalc_cursor(s);
ctx->win->dirty = 1;
(void)e0;
}
static void editor_on_draw(void *state, app_ctx *ctx) {
editor_state *s = (editor_state *)state;
int cx = ctx->client_x;
int cy = ctx->client_y;
int cw = ctx->client_w;
int ch = ctx->client_h;
int line_num_w = 5 * (int)DU_ASCII_STEP + (int)DU_SPACE_SM;
int text_area_x = cx + line_num_w;
int text_area_w = cw - line_num_w;
int visible_rows = ch / (int)DU_ASCII_LINE_H;
int visible_cols = text_area_w / (int)DU_ASCII_STEP;
/* 背景 */
du_fill_rect(&g_fb, cx, cy, cw, ch, DS_DARK_BG_SECONDARY);
/* 自动滚动 */
if (s->cursor_line < s->scroll_y) s->scroll_y = s->cursor_line;
if (s->cursor_line >= s->scroll_y + visible_rows) s->scroll_y = s->cursor_line - visible_rows + 1;
/* 渲染可见行 */
int line = 0;
int pos = 0;
for (int i = 0; i < s->text_len || line <= s->cursor_line; ) {
if (line >= s->scroll_y + visible_rows) break;
if (line >= s->scroll_y) {
int screen_y = cy + (line - s->scroll_y) * (int)DU_ASCII_LINE_H;
/* 行号 */
char ln[8];
ln[0] = '0' + ((line+1)/100)%10;
ln[1] = '0' + ((line+1)/10)%10;
ln[2] = '0' + (line+1)%10;
ln[3] = 0;
du_draw_string(&g_fb, ln, cx + 2, screen_y,
DS_DARK_TEXT_DIM, DS_DARK_BG_SECONDARY, DU_ASCII_STEP);
/* 行号分隔线 */
du_fill_rect(&g_fb, cx + line_num_w - 2, screen_y, 1,
(int)DU_ASCII_CELL_H, DS_DARK_DIVIDER);
/* 行文本 */
int col = 0;
while (pos < s->text_len && s->text[pos] != '\n' && col < visible_cols) {
du_draw_char(&g_fb, s->text[pos],
text_area_x + col * (int)DU_ASCII_STEP,
screen_y,
DS_DARK_TEXT_PRIMARY, DS_DARK_BG_SECONDARY);
col++; pos++;
}
if (pos < s->text_len && s->text[pos] == '\n') pos++;
} else {
while (pos < s->text_len && s->text[pos] != '\n') pos++;
if (pos < s->text_len) pos++;
}
line++;
if (pos >= s->text_len && line > s->cursor_line) break;
}
/* 光标 */
if (s->cursor_line >= s->scroll_y && s->cursor_line < s->scroll_y + visible_rows) {
int cursor_y = cy + (s->cursor_line - s->scroll_y) * (int)DU_ASCII_LINE_H;
int cursor_x = text_area_x + s->cursor_col * (int)DU_ASCII_STEP;
du_fill_rect(&g_fb, cursor_x, cursor_y, 2, (int)DU_ASCII_CELL_H, DS_DARK_CURSOR);
}
/* 状态栏 */
int status_y = cy + ch - 20;
du_fill_rect(&g_fb, cx, status_y, cw, 20, DP_ABYSS_800);
char status[64];
int sp = 0;
/* 行:列 */
const char *p1 = "L:"; while(*p1) status[sp++] = *p1++;
status[sp++] = '0' + (s->cursor_line+1)/10%10;
status[sp++] = '0' + (s->cursor_line+1)%10;
const char *p2 = " C:"; while(*p2) status[sp++] = *p2++;
status[sp++] = '0' + (s->cursor_col+1)/10%10;
status[sp++] = '0' + (s->cursor_col+1)%10;
if (s->modified) { const char *m = " *"; while(*m) status[sp++] = *m++; }
status[sp] = 0;
du_draw_string(&g_fb, status, cx + 4, status_y + 1,
DS_DARK_TEXT_DIM, DP_ABYSS_800, DU_ASCII_STEP);
}
static void editor_on_destroy(void *state) { (void)state; }
/* ============================================================
* 内置应用:计算器 (Calculator)
* ============================================================ */
typedef struct {
char display[32];
int display_len;
i64 accumulator;
i64 current;
int op; /* 0=none, 1=+, 2=-, 3=*, 4=/ */
int new_number;
} calc_state;
static void calc_update_display(calc_state *s) {
s->display_len = 0;
i64 val = s->current;
if (val < 0) { s->display[s->display_len++] = '-'; val = -val; }
if (val == 0) { s->display[s->display_len++] = '0'; }
else {
char tmp[20]; int tl = 0;
while (val > 0) { tmp[tl++] = '0' + (int)(val % 10); val /= 10; }
for (int i = tl-1; i >= 0; i--) s->display[s->display_len++] = tmp[i];
}
s->display[s->display_len] = 0;
}
static void *calc_on_create(app_ctx *ctx) {
calc_state *s = (calc_state *)0x6000000;
s->accumulator = 0; s->current = 0;
s->op = 0; s->new_number = 1;
calc_update_display(s);
const char *t = "Calculator";
for (int i = 0; t[i] && i < 47; i++) ctx->win->title[i] = t[i];
return s;
}
static void calc_on_event(void *state, app_ctx *ctx, desktop_event *ev) {
calc_state *s = (calc_state *)state;
if (ev->type != EV_KEY_DOWN) return;
char c = scan_to_ascii(ev->scancode, ev->shift);
if (c >= '0' && c <= '9') {
if (s->new_number) { s->current = 0; s->new_number = 0; }
s->current = s->current * 10 + (c - '0');
} else if (c == '+') { s->accumulator = s->current; s->op = 1; s->new_number = 1; }
else if (c == '-') { s->accumulator = s->current; s->op = 2; s->new_number = 1; }
else if (c == '*') { s->accumulator = s->current; s->op = 3; s->new_number = 1; }
else if (c == '/') { s->accumulator = s->current; s->op = 4; s->new_number = 1; }
else if (c == '=' || c == '\n') {
switch (s->op) {
case 1: s->current = s->accumulator + s->current; break;
case 2: s->current = s->accumulator - s->current; break;
case 3: s->current = s->accumulator * s->current; break;
case 4: s->current = (s->current != 0) ? s->accumulator / s->current : 0; break;
}
s->op = 0; s->new_number = 1;
} else if (c == 'c' || c == 'C') {
s->current = 0; s->accumulator = 0; s->op = 0; s->new_number = 1;
}
calc_update_display(s);
ctx->win->dirty = 1;
}
static void calc_on_draw(void *state, app_ctx *ctx) {
calc_state *s = (calc_state *)state;
int cx = ctx->client_x;
int cy = ctx->client_y;
int cw = ctx->client_w;
int ch = ctx->client_h;
du_fill_rect(&g_fb, cx, cy, cw, ch, DS_DARK_BG_SECONDARY);
/* 显示屏 */
int disp_h = 40;
du_fill_rounded_rect(&g_fb, cx + 8, cy + 8, cw - 16, disp_h,
DP_ABYSS_900, DU_RADIUS_MD);
du_rect_outline(&g_fb, cx + 8, cy + 8, cw - 16, disp_h,
DS_DARK_BORDER, DU_RADIUS_MD);
/* 数字右对齐 */
int text_x = cx + cw - 16 - s->display_len * (int)DU_ASCII_STEP - 8;
du_draw_string(&g_fb, s->display, text_x, cy + 16,
DS_DARK_TEXT_PRIMARY, DP_ABYSS_900, DU_ASCII_STEP);
/* 按钮网格 4×4 */
const char *btn_labels[] = {
"C", "+/-", "%", "/",
"7", "8", "9", "*",
"4", "5", "6", "-",
"1", "2", "3", "+",
"0", ".", "=", 0
};
int btn_w = (cw - 16 - 12) / 4;
int btn_h = 32;
int btn_y = cy + 8 + disp_h + 8;
for (int i = 0; i < 19; i++) {
int row = i / 4, col = i % 4;
int bx = cx + 8 + col * (btn_w + 3);
int by = btn_y + row * (btn_h + 3);
if (by + btn_h > cy + ch - 4) break;
u32 bg = (i < 4) ? DP_ABYSS_600 :
(i % 4 == 3) ? DS_DARK_ACCENT :
(i == 18) ? DP_SUCCESS :
DP_ABYSS_700;
du_fill_rounded_rect(&g_fb, bx, by, btn_w, btn_h, bg, DU_RADIUS_SM);
du_draw_string(&g_fb, btn_labels[i],
bx + (btn_w - 1*(int)DU_ASCII_STEP)/2,
by + (btn_h - (int)DU_ASCII_CELL_H)/2,
DS_DARK_TEXT_PRIMARY, bg, DU_ASCII_STEP);
}
}
static void calc_on_destroy(void *state) { (void)state; }
/* ============================================================
* 应用注册
* ============================================================ */
static void register_apps(void) {
/* 0: Terminal */
g_apps[0] = (app_descriptor){
"bash", "Terminal", 640, 440,
bash_on_create, bash_on_event, bash_on_draw, bash_on_destroy
};
g_app_count++;
/* 1: Editor */
g_apps[1] = (app_descriptor){
"editor", "Editor", 600, 450,
editor_on_create, editor_on_event, editor_on_draw, editor_on_destroy
};
g_app_count++;
/* 2: File Manager */
g_apps[2] = (app_descriptor){
"fileman", "Files", 500, 400,
0, 0, 0, 0 /* placeholder */
};
g_app_count++;
/* 3: Calculator */
g_apps[3] = (app_descriptor){
"calc", "Calculator", 280, 400,
calc_on_create, calc_on_event, calc_on_draw, calc_on_destroy
};
g_app_count++;
}
static void setup_desktop_icons(void) {
int start_x = (int)g_fb_w - DESKTOP_ICON_W - ICON_GUTTER_X;
int y = ICON_GUTTER_Y;
for (int i = 0; i < g_app_count; i++) {
if (!g_apps[i].on_create) continue;
g_icons[g_icon_count].app_id = i;
g_icons[g_icon_count].x = start_x;
g_icons[g_icon_count].y = y;
g_icons[g_icon_count].label = g_apps[i].display_name;
g_icon_count++;
y += DESKTOP_ICON_H;
}
}
/* ============================================================
* 应用启动
* ============================================================ */
static void launch_app(int app_id) {
if (app_id < 0 || app_id >= g_app_count) return;
app_descriptor *app = &g_apps[app_id];
if (!app->on_create) return;
/* 计算窗口位置(居中偏移) */
int wx = 80 + g_win_count * 30;
int wy = 40 + g_win_count * 30;
if (wx + (int)app->default_w > (int)g_fb_w - 20) wx = 80;
if (wy + (int)app->default_h > (int)g_fb_h - TASKBAR_H - 20) wy = 40;
desktop_window *win = win_create(app_id, app->display_name,
wx, wy, (int)app->default_w, (int)app->default_h);
if (!win) return;
/* 构建应用上下文 */
app_ctx ctx;
ctx.fb = &g_fb;
ctx.win = win;
ctx.client_x = win->x + BORDER_W;
ctx.client_y = win->y + TITLEBAR_H;
ctx.client_w = win->w - 2 * BORDER_W;
ctx.client_h = win->h - TITLEBAR_H - BORDER_W;
ctx.should_exit = 0;
ctx.block_read = g_block_read;
ctx.kernel_api = g_kernel_api;
win->app_state = app->on_create(&ctx);
win->dirty = 1;
slog("app launched");
}
/* ============================================================
* 全屏重绘
* ============================================================ */
static void redraw_all(void) {
/* 渐变背景 */
du_fill_bg_gradient(&g_fb, DP_ABYSS_900, DP_ABYSS_800);
/* 桌面图标 */
draw_desktop_icons();
/* 窗口(从底到顶) */
for (int i = 0; i < g_win_count; i++) {
desktop_window *w = &g_windows[i];
if (w->minimized || !w->visible) continue;
/* 窗口客户区背景 */
int cx = w->x + BORDER_W;
int cy = w->y + TITLEBAR_H;
int cw = w->w - 2 * BORDER_W;
int ch = w->h - TITLEBAR_H - BORDER_W;
/* 调用应用绘制 */
if (w->app_id >= 0 && w->app_id < g_app_count && g_apps[w->app_id].on_draw && w->app_state) {
app_ctx ctx;
ctx.fb = &g_fb;
ctx.win = w;
ctx.client_x = cx; ctx.client_y = cy;
ctx.client_w = cw; ctx.client_h = ch;
ctx.should_exit = 0;
ctx.block_read = g_block_read;
ctx.kernel_api = g_kernel_api;
g_apps[w->app_id].on_draw(w->app_state, &ctx);
}
/* 窗口装饰(在应用内容之上) */
draw_window_frame(w);
}
/* 任务栏 */
draw_taskbar();
/* 鼠标光标 */
cursor_save_bg(g_mouse_x, g_mouse_y);
cursor_draw(g_mouse_x, g_mouse_y);
}
/* ============================================================
* 主循环
* ============================================================ */
__attribute__((visibility("default")))
void dsk_entry(const dsk_boot_context *ctx) {
__asm__ volatile("cli");
slog("boot");
if (!ctx || ctx->magic != 0x44534B31424F4F54ULL) {
slog("bad context");
for(;;) __asm__("hlt");
}
g_fb_addr = ctx->framebuffer_address;
g_fb_w = ctx->framebuffer_width;
g_fb_h = ctx->framebuffer_height;
g_fb_pitch = ctx->framebuffer_pitch;
g_kernel_api = (const void *)ctx->dkm_kernel_api;
g_block_read = (desktop_block_read_fn)(*(u64 *)(ctx->dkm_kernel_api + 0xA8));
du_context_init(&g_fb, g_fb_addr, g_fb_w, g_fb_h, g_fb_pitch);
/* 初始化 PS/2 鼠标 */
ps2_mouse_init();
/* 注册应用 */
register_apps();
setup_desktop_icons();
/* 初始绘制 */
redraw_all();
slog("desktop ready");
/* 主事件循环 */
for (;;) {
int need_redraw = 0;
g_mouse_has_pkt = 0;
/* 轮询 PS/2 鼠标 */
if (ps2_mouse_poll()) {
need_redraw = 1;
/* 拖拽处理 */
if (g_dragging && g_drag_win >= 0) {
desktop_window *w = win_find(g_drag_win);
if (w) {
w->x = g_mouse_x - g_drag_off_x;
w->y = g_mouse_y - g_drag_off_y;
}
}
/* 鼠标点击事件 */
if (g_mouse_btn) {
/* 检查任务栏点击 */
int tb_y = (int)g_fb_h - TASKBAR_H;
if (g_mouse_y >= tb_y) {
/* 快速启动图标 */
int qx = 64;
for (int i = 0; i < g_app_count && i < 4; i++) {
int icon_x = qx + i * 40;
if (g_mouse_x >= icon_x && g_mouse_x < icon_x + 40) {
launch_app(i);
need_redraw = 1;
break;
}
}
} else {
/* 窗口命中测试 */
desktop_window *hit = win_find_at(g_mouse_x, g_mouse_y);
if (hit) {
win_bring_to_front(hit);
/* 取消其他焦点 */
for (int i = 0; i < g_win_count; i++)
g_windows[i].focused = (g_windows[i].id == hit->id);
g_focused_win = hit->id;
/* 检查关闭按钮 */
if (win_hit_close_btn(hit, g_mouse_x, g_mouse_y)) {
win_destroy(hit);
need_redraw = 1;
}
/* 检查标题栏拖拽 */
else if (win_hit_titlebar(hit, g_mouse_x, g_mouse_y)) {
g_dragging = 1;
g_drag_win = hit->id;
g_drag_off_x = g_mouse_x - hit->x;
g_drag_off_y = g_mouse_y - hit->y;
}
/* 传递鼠标事件给应用 */
else if (hit->app_id >= 0 && g_apps[hit->app_id].on_event && hit->app_state) {
desktop_event dev;
dev.type = EV_MOUSE_DOWN;
dev.mx = g_mouse_x; dev.my = g_mouse_y;
dev.button = 0;
app_ctx actx;
actx.fb = &g_fb; actx.win = hit;
actx.client_x = hit->x + BORDER_W;
actx.client_y = hit->y + TITLEBAR_H;
actx.client_w = hit->w - 2*BORDER_W;
actx.client_h = hit->h - TITLEBAR_H - BORDER_W;
actx.should_exit = 0;
actx.block_read = g_block_read;
actx.kernel_api = g_kernel_api;
g_apps[hit->app_id].on_event(hit->app_state, &actx, &dev);
}
} else {
/* 桌面图标点击 */
for (int i = 0; i < g_icon_count; i++) {
int ix = g_icons[i].x, iy = g_icons[i].y;
if (g_mouse_x >= ix && g_mouse_x < ix + 32 &&
g_mouse_y >= iy && g_mouse_y < iy + 32) {
launch_app(g_icons[i].app_id);
need_redraw = 1;
break;
}
}
}
}
} else {
if (g_dragging) {
g_dragging = 0;
g_drag_win = -1;
}
}
}
/* 轮询 PS/2 键盘 */
{
u8 st = inb(0x64);
if ((st & 1) && !(st & 0x20)) {
u8 data = inb(0x60);
u8 sc = data;
if (sc == 0xE0) { g_e0 = 1; goto next; }
if (sc == 0x2A || sc == 0x36) { g_shift = 1; goto next; }
if (sc == 0xAA || sc == 0xB6) { g_shift = 0; goto next; }
if (sc & 0x80) { g_e0 = 0; goto next; }
/* 发送键盘事件到焦点窗口 */
if (g_focused_win >= 0) {
desktop_window *fw = win_find(g_focused_win);
if (fw && fw->app_id >= 0 && g_apps[fw->app_id].on_event && fw->app_state) {
desktop_event dev;
dev.type = EV_KEY_DOWN;
dev.mx = g_mouse_x; dev.my = g_mouse_y;
dev.button = 0;
dev.scancode = sc;
dev.shift = g_shift;
dev.ctrl = g_ctrl;
dev.alt = g_alt;
app_ctx actx;
actx.fb = &g_fb; actx.win = fw;
actx.client_x = fw->x + BORDER_W;
actx.client_y = fw->y + TITLEBAR_H;
actx.client_w = fw->w - 2*BORDER_W;
actx.client_h = fw->h - TITLEBAR_H - BORDER_W;
actx.should_exit = 0;
actx.block_read = g_block_read;
actx.kernel_api = g_kernel_api;
g_apps[fw->app_id].on_event(fw->app_state, &actx, &dev);
need_redraw = 1;
}
}
g_e0 = 0;
}
}
next:
if (need_redraw) {
cursor_restore_bg();
redraw_all();
}
__asm__("pause");
}
}