Files
Deshab-OS/RE/docs/desktop_architecture.md
T

462 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Deshab 桌面环境架构设计
## 1. 概述
Deshab 桌面环境(Deshab Desktop Environment, DDE)是基于 UTSM/DSK 内核的图形桌面系统,
运行在 SAS-R0(单地址空间 Ring0)任务模型上,使用 "Sealed Arc" 视觉风格系统。
**设计原则**:
- **SAS-R0 适配**:无内存隔离,所有应用共享地址空间,信任模型为 Ring0 全信任
- **单屏幕协作**:初期采用"多窗口前台 + 桌面后台"模式,桌面管理器始终驻留
- **应用独立开发**:内置应用使用回调模式;未来工具应用为独立 .elf,通过 ELF 加载方式载入
- **帧缓冲直绘**:无 GPU 加速,所有渲染通过 du_context 直接写 framebuffer
**当前实现状态**:desktop.elf 已实现完整的桌面管理器框架,包括窗口管理、事件路由、
PS/2 鼠标初始化+轮询、光标 save/restore、任务栏、桌面图标和 3 个内置应用。
## 2. 系统架构
```
┌─────────────────────────────────────────────────────┐
│ Framebuffer │
│ ┌─────────────────────────────────────────────┐ │
│ │ Desktop Manager (desktop.elf) │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ Window 1 │ │ Window 2 │ │ Window 3 │ │ │
│ │ │ (bash) │ │ (editor) │ │ (calc) │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────┐│ │
│ │ │ Taskbar (40px) ││ │
│ │ └─────────────────────────────────────────┘│ │
│ └─────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
```
## 3. 启动流程
```
UEFI → Limine → utsm.elf → DKM 驱动加载
→ deshab.elf (DSK) → spinner → firstInit.txt 检查
→ mouseInit.elf → netman.elf → [首次] FirstInit.elf
→ desktop.elf ← 桌面管理器
```
DSK 在 FirstInit 完成或 firstInit=1 时,加载 `desktop.elf`。
desktop.elf 通过 `dsk_entry(dsk_boot_context *ctx)` 入口接收系统上下文,
自行初始化 PS/2 鼠标、注册应用、进入主事件循环。
## 4. Desktop Manager 核心设计
### 4.1 窗口模型
```c
typedef struct desktop_window {
int id; /* 窗口 ID(自增分配) */
int x, y, w, h; /* 窗口位置和尺寸 */
int visible; /* 可见性 */
int focused; /* 焦点 */
int minimized; /* 最小化 */
char title[48]; /* 窗口标题 */
/* 应用关联 */
int app_id; /* 关联的应用 ID(g_apps 索引) */
void *app_state; /* 应用私有状态(on_create 返回值) */
int closing; /* 关闭中标记 */
int dirty; /* 脏标记(需要重绘) */
} desktop_window;
```
**Z 序**:窗口按数组顺序排列,数组末尾的窗口在最上层。`win_bring_to_front()` 将窗口移到数组末尾。
**窗口限制**:最多 8 个窗口(`MAX_WINDOWS`)。
**固定地址分配**:当前内置应用使用固定地址分配状态结构,避免堆依赖:
- `0x4000000` — bash_state(终端)
- `0x5000000` — editor_state(编辑器)
- `0x6000000` — calc_state(计算器)
### 4.2 事件系统
```c
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;
```
**事件路由**:
1. **鼠标事件** → 从后往前命中测试 → 找到目标窗口 → 检查关闭按钮/标题栏/客户区 → 路由到窗口回调
2. **键盘事件** → 当前焦点窗口的 `on_event` 回调
3. **任务栏点击** → 桌面管理器直接处理(快速启动/窗口按钮)
4. **桌面图标点击** → 桌面管理器处理(`launch_app()`)
**拖拽机制**:
- 鼠标按下 + 命中标题栏 → 进入拖拽模式(`g_dragging=1`)
- 拖拽期间鼠标移动 → 更新窗口位置(`w->x = mouse_x - offset`)
- 鼠标释放 → 退出拖拽模式
**关闭机制**:
- 鼠标点击窗口右上角红色 X 按钮(24×20 圆角矩形,DP_ERROR 底色)
- `win_destroy()` 调用应用的 `on_destroy`,从窗口数组移除,焦点转移到最后一个窗口
### 4.3 应用 ABI(回调模式)
```c
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; /* FAT32 块读取函数 */
const void *kernel_api; /* DKM 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; /* UI 显示名 */
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;
```
**应用生命周期**:
1. 用户点击桌面图标或快速启动 → `launch_app(app_id)`
2. 创建窗口 → 构建 `app_ctx` → 调用 `on_create(ctx)` → 返回 `app_state`
3. 事件循环中:`on_event(state, ctx, ev)` 处理输入
4. 绘制时:`on_draw(state, ctx)` 渲染客户区
5. 关闭窗口 → `on_destroy(state)` 清理
**当前注册的应用**:
| app_id | name | display_name | 默认尺寸 | 状态 |
|--------|------|-------------|----------|------|
| 0 | bash | Terminal | 640×440 | 已实现 |
| 1 | editor | Editor | 600×450 | 已实现 |
| 2 | fileman | Files | 500×400 | 占位(无回调) |
| 3 | calc | Calculator | 280×400 | 已实现 |
### 4.4 任务栏 (Taskbar)
位置:屏幕底部,高度 40px。
布局:
```
┌──────────────────────────────────────────────────────────┐
│ [D] │ 🖥️ 📝 📁 🧮 │ │ 12:34 │
│Start│ 快速启动(4图标) │ 窗口按钮 │ RTC时钟 │
└──────────────────────────────────────────────────────────┘
```
分区:
- **Start 按钮**(48×32,DP_ABYSS_600 圆角矩形):显示 "D" 字符(DS_DARK_ACCENT_LIGHT)
- **快速启动**(4 图标 × 40px):按 app_id 顺序绘制程序化图标(终端/编辑器/文件夹/计算器)
- **窗口按钮区**:每个打开窗口一个 100×32 按钮,焦点窗口用 DS_DARK_ACCENT 底色,其余用 DP_ABYSS_600
- **RTC 时钟**(右侧 80px 区域):通过 CMOS I/O(端口 0x70/0x71)读取 BCD 时分,转换为 HH:MM 格式
### 4.5 桌面图标
位置:桌面右侧垂直排列。
参数:
- 列宽 80px(`DESKTOP_ICON_W`),行高 72px(`DESKTOP_ICON_H`)
- 左边距 16px(`ICON_GUTTER_X`),上边距 16px(`ICON_GUTTER_Y`)
- 起始 X = `fb_width - 80 - 16`
图标:32×32 像素,程序化生成(无需位图文件),通过 `draw_icon_*` 函数绘制:
- **终端图标**:DP_ABYSS_700 圆角矩形 + DP_SEAL_300 的 >_ 符号 + DP_SEAL_500 边框
- **编辑器图标**:DP_ABYSS_700 圆角矩形 + DP_SEAL_500 文本行 + DP_SEAL_300 边框
- **文件夹图标**:DP_ARC_500 圆角矩形 + DP_ARC_300 文件夹形状 + DP_ARC_700 边框
- **计算器图标**:DP_ABYSS_700 圆角矩形 + DP_WARNING 按键 + DP_NEUTRAL_400 网格
标签:图标下方 36px 处,居中绘制应用 display_name,使用 DS_DARK_TEXT_PRIMARY 颜色。
### 4.6 窗口装饰
**标题栏**(28px,`TITLEBAR_H`):
- 焦点窗口:DP_ABYSS_600 背景
- 非焦点窗口:DP_ABYSS_800 背景
- 标题文字:左侧 DU_SPACE_SM 偏移,DS_DARK_TEXT_PRIMARY 颜色
- 关闭按钮:右上角,窗口右边 -28px 处,24×20 圆角矩形,DP_ERROR (红色) 底色 + 白色 "X"
**边框**(2px,`BORDER_W`):
- 焦点窗口:DS_DARK_BORDER(Seal Cyan 发光效果)
- 非焦点窗口:DS_DARK_BORDER
- 焦点发光:`du_rect_glow()` 3px 扩展范围,10px 衰减
**标题栏底线**:1px 分隔线,颜色同边框色。
**圆角**:窗口主体使用 `DU_RADIUS_LG` 圆角,关闭按钮使用 `DU_RADIUS_SM` 圆角。
### 4.7 应用加载模型
**方式一:回调式(当前实现)**
应用编译进 desktop.elf 内部,通过 `app_descriptor` 注册回调函数。
桌面管理器在事件循环中调用 `on_event()`/`on_draw()`,应用通过 `app_ctx` 访问系统服务。
特点:
- 应用与桌面管理器在同一 ELF 中,共享代码和全局变量
- 应用状态使用固定地址分配(避免堆依赖)
- 应用可以直接访问 g_fb 全局帧缓冲
**方式二:独立 ELF 加载式(未来实现)**
工具应用独立开发为单独 .elf 文件(如 `CODE/tools/bash/`),通过 DSK 的 FAT32 + ELF loader 加载。
独立工具实现 `dsk_entry(dsk_boot_context *ctx)` 入口点,接收与 DSK 相同的系统上下文。
未来桌面管理器将通过 ELF 加载方式载入工具 .elf,需要实现:
- ELF 加载到内存 + PIE 重定位
- 传入 dsk_boot_context(framebuffer、kernel_api、block_read 等)
- 返回机制(在 ELF 入口点保存返回地址)
- 独立工具的公共辅助代码(`CODE/tools/desktop_app.h`)
## 5. 渲染管线
```
redraw_all() 完整管线:
1. 渐变背景(du_fill_bg_gradient: DP_ABYSS_900 → DP_ABYSS_800)
2. 桌面图标(draw_desktop_icons)
3. 窗口循环(从底到顶):
a. 应用 on_draw() 绘制客户区内容
b. draw_window_frame() 绘制窗口装饰(在应用内容之上)
4. 任务栏(draw_taskbar)
5. 鼠标光标(cursor_save_bg + cursor_draw)
```
**渲染策略**:
- 每帧全屏重绘(无增量更新)
- 鼠标光标使用 save/restore 机制:移动前恢复旧位置背景,重绘后保存新位置背景
- 应用直接写入全局 framebuffer(无窗口后缓冲),窗口装饰在应用内容之上绘制
- 重绘触发:鼠标移动/点击、键盘事件、窗口状态变化
## 6. 像素格式
与现有 framebuffer 一致:32bpp ARGB(0xAARRGGBB),小端存储。
所有颜色值使用 deshab_ui.h 中的语义色令牌。
## 7. 鼠标光标
24×24 像素箭头光标:
- 热点在左上角 (0,0)
- 形状定义:`cursor_shape[24][24]` 位图数组(1=前景,0=透明)
- 前景色:DS_DARK_TEXT_PRIMARY
- save/restore 实现无闪烁移动:
- `cursor_save_bg()`: 保存光标位置 24×24 像素到 `g_cursor_save[]`
- `cursor_restore_bg()`: 恢复上次保存的像素
- 每帧重绘时:先 restore 旧位置 → 全屏绘制 → save + draw 新位置
- 边界限制:光标不超出屏幕范围(减去 CURSOR_SIZE)
## 8. 输入处理
### 8.1 PS/2 鼠标
**初始化**(`ps2_mouse_init()`):
1. 启用 AUX 设备(命令 0xA8 → 0x64)
2. 读取控制器配置字节(0x20 → 0x64),设置 bit1(AUX IRQ12)+ 清除 bit5(AUX clock)
3. 写回配置(0x60 → 0x64)
4. 复位鼠标(0xD4 → 0x64,0xFF → 0x60)
5. 启用 streaming(0xD4 → 0x64,0xF4 → 0x60)
**轮询**(`ps2_mouse_poll()`):
- 读取端口 0x64 状态,检查 bit0(数据就绪)和 bit5(AUX 标记)
- 读取 0x60 数据,累积 3 字节组成一个鼠标包
- 校验 byte0 bit3 同步位
- 解析 X/Y 位移(带符号扩展)和左键状态(byte0 bit0)
- Y 轴翻转处理(鼠标 Y 向上为正,屏幕 Y 向下为正)
- 边界裁剪:光标限制在屏幕范围内
### 8.2 PS/2 键盘
- IRQ1 → scan code set 1
- 扩展键 E0 前缀追踪(`g_e0` 标记)
- Shift 修饰键追踪(0x2A/0x36 按下,0xAA/0xB6 释放)
- `scan_to_ascii()` 映射:normal[58] + shifted[58] 查找表
- 键盘事件发送到焦点窗口的 `on_event` 回调
**全局快捷键**(规划中):
- Alt+Tab:窗口切换
- Alt+F4:关闭窗口
## 9. 文件系统接口
工具应用通过 `app_ctx.block_read` 访问 FAT32 文件系统。
`block_read` 函数类型:
```c
typedef int (*desktop_block_read_fn)(void *ctx, unsigned long long lba, unsigned int count, void *buffer);
```
从 `dkm_kernel_api + 0xA8` 偏移处获取函数指针。
暂不实现 VFS 系统调用——所有文件操作通过直接 FAT32 路径解析完成。
## 10. 网络接口
网络工具(curl、ping、browser)依赖 netman 已实现的:
- DHCP 客户端
- ARP 解析
- DNS 查询
- e1000/virtio_net TX/RX
工具应用通过 `kernel_api.net` 访问网络功能。
## 11. 内置应用详情
### 11.1 终端 (Bash)
- 状态结构:`bash_state` @ 0x4000000
- 终端缓冲:80 列 × 40 行(TERM_MAX_COLS × TERM_MAX_ROWS)
- 每格存储:字符(u8) + 前景色(u32)
- 输入缓冲:256 字节,支持插入模式和光标移动
- 提示符:`deshab# `
- 命令:help, clear, echo, version/ver, uname, date, about, reboot, halt, pwd, whoami, id
- 渲染:先绘制历史内容,再绘制当前输入行+光标
- 光标:2px 高下划线,DS_DARK_CURSOR 颜色
- 配色:DS_DARK_* 暗色主题
### 11.2 文本编辑器 (Editor)
- 状态结构:`editor_state` @ 0x5000000
- 文本缓冲:65536 字节(EDITOR_BUF_SIZE)
- 编辑操作:插入/删除字符、换行、方向键移动、Home/End
- 行号:3 位数字 + 分隔线(DS_DARK_TEXT_DIM 颜色)
- 自动滚动:光标超出可视区域时调整 scroll_y
- 状态栏:底部 20px,显示 L:行 C:列 + 修改标记(*)
- 光标:2px 宽竖线,DS_DARK_CURSOR 颜色
- 编辑区背景:DS_DARK_BG_SECONDARY
- 窗口标题:`Editor - untitled`
### 11.3 计算器 (Calculator)
- 状态结构:`calc_state` @ 0x6000000
- 运算:四则运算(+/-/×/÷),i64 精度
- 显示屏:圆角矩形,右对齐数字
- 按钮网格:4×5 布局(C/+/-/%/÷, 7/8/9/×, 4/5/6/-, 1/2/3/+, 0/./=)
- 按键颜色:功能键 DP_ABYSS_600,运算符 DS_DARK_ACCENT,等号 DP_SUCCESS
- 输入:键盘数字键 + 运算符,C 清零,Enter/= 计算
## 12. 分阶段实现计划
### Phase 1 — 桌面基础(已完成)
- ✅ Desktop Manager 核心框架
- ✅ 任务栏 + 时钟 + 桌面图标
- ✅ 窗口管理(创建/销毁/移动/焦点/拖拽)
- ✅ 事件路由系统
- ✅ PS/2 鼠标初始化 + 轮询
- ✅ 光标 save/restore
- ✅ 终端应用(Bash)
- ✅ 文本编辑器(Editor)
- ✅ 计算器(Calculator)
### Phase 2 — 文件操作与独立应用
- 工具应用独立开发框架(CODE/tools/ + desktop_app.h)
- FAT32 路径解析增强(目录遍历)
- 文件管理器(独立 .elf)
- 编辑器独立化(从内置 → 独立 tools/editor/)
- 文件打开器(关联 .txt → 编辑器)
- Ctrl+S 保存(需 FAT32 write API)
### Phase 3 — 网络
- TCP/IP 最小实现(基于 e1000/virtio_net)
- ping 工具(ICMP Echo)
- curl 工具(HTTP GET)
### Phase 4 — 高级应用
- 浏览器(HTTP + 简单 HTML 渲染)
- Bash 增强版(命令历史↑↓、ls、cat、Tab 补全)
- 更多系统工具
## 13. 目录结构
```
CODE/
desktop/ 桌面管理器(当前实现)
main.c 主循环、事件路由、compositing、3个内置应用
linker.ld PIE 链接脚本
MAKEFILE 构建脚本(输出 desktop.elf)
tools/ 独立工具应用(未来实现)
desktop_app.h 公共辅助头文件
bash/ 独立终端
main.c
MAKEFILE
linker.ld
editor/ 独立文本编辑器
main.c
MAKEFILE
linker.ld
fileman/ 文件管理器
main.c
MAKEFILE
linker.ld
browser/ 浏览器
main.c
MAKEFILE
linker.ld
curl/ HTTP 客户端
main.c
MAKEFILE
linker.ld
ping/ ICMP 工具
main.c
MAKEFILE
linker.ld
```
## 14. 构建与打包
### 当前构建
desktop.elf 编译输出到 `SYSTEM/system/deshab64/desktop.elf`。
编译参数(与 DSK / shell / FirstInit 一致):
```
clang -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin
-fno-stack-protector -fpie -mno-red-zone -mno-sse -mno-sse2
-mno-mmx -msoft-float -m64 -Wall -Wextra -I../UTSM/include -O2
ld.lld -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld
```
### 未来工具应用构建
独立工具 .elf 将输出到 `SYSTEM/system/deshab64/tools/`,构建集成:
- 每个工具有独立 MAKEFILE(与 desktop 相同模式)
- build.ps1 中添加工具构建步骤
- mkfat32.c 中添加工具 .elf 入口
```
SYSTEM/system/deshab64/
desktop.elf ← 桌面管理器
tools/
bash.elf ← 独立终端
editor.elf ← 独立文本编辑器
fileman.elf ← 文件管理器
browser.elf ← 浏览器
curl.elf ← HTTP 客户端
ping.elf ← ICMP 工具
```
DSK 启动流程:
- 首次启动:mouseInit → netman → FirstInit → desktop
- 非首次启动:mouseInit → netman → desktop