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
+5
View File
@@ -1 +1,6 @@
ISO/deshab.img ISO/deshab.img
*.o
.build_tmp/
.trae/
docs/workbuddy-planning/
sata_fat32_dsk.img
+117 -2
View File
@@ -1,11 +1,126 @@
/* process.c — UTSM 进程上下文管理
*
* 进程表分配/释放、capability table 操作、crypto context 管理。
*/
#include <utsm/process.h> #include <utsm/process.h>
#include <utsm/drr.h> #include <utsm/drr.h>
#include <utsm/arena.h>
#include <utsm/log.h>
/* ---- 进程表 ---- */
static utsm_process_context g_process_table[UTSM_MAX_PROCESSES];
static u32 g_process_count;
void utsm_process_init(void) {
log_info("[UTSM] process table init");
for (u32 i = 0; i < UTSM_MAX_PROCESSES; i++) {
g_process_table[i].state = UTSM_PROC_UNUSED;
g_process_table[i].process_slot = i;
g_process_table[i].cap_table.count = 0;
g_process_table[i].cap_table.generation = 0;
}
g_process_count = 0;
}
void utsm_process_create_test(utsm_process_context *process, u64 seed) { void utsm_process_create_test(utsm_process_context *process, u64 seed) {
process->process_uuid = uuid_make_test(seed); process->process_uuid = uuid_make_test(seed);
process->crypto_epoch = 1; process->crypto_epoch = 1;
process->recovery_generation = drr_recovery_generation(); process->recovery_generation = drr_recovery_generation();
process->capability_table_slot = 0;
process->capability_count = 0; process->capability_count = 0;
process->flags = 0; process->flags = UTSM_PROC_F_KERNEL;
process->state = UTSM_PROC_ACTIVE;
process->hot_segment_slot = 0;
process->drr_recovery_gen = drr_recovery_generation();
/* 初始化 crypto context */
process->crypto_ctx.crypto_epoch = 1;
process->crypto_ctx.key_derivation_seed = seed ^ 0x9e3779b97f4a7c15ULL;
process->crypto_ctx.pckc_hot_hint = 0;
/* 初始化 capability table */
process->cap_table.count = 0;
process->cap_table.generation = 1;
}
utsm_process_context *utsm_process_alloc(void) {
for (u32 i = 0; i < UTSM_MAX_PROCESSES; i++) {
if (g_process_table[i].state == UTSM_PROC_UNUSED) {
utsm_process_context *p = &g_process_table[i];
p->process_slot = i;
p->state = UTSM_PROC_ACTIVE;
p->crypto_epoch = 1;
p->recovery_generation = drr_recovery_generation();
p->capability_count = 0;
p->flags = 0;
p->hot_segment_slot = 0;
p->drr_recovery_gen = drr_recovery_generation();
p->cap_table.count = 0;
p->cap_table.generation = 1;
p->crypto_ctx.crypto_epoch = 1;
p->crypto_ctx.key_derivation_seed = 0;
p->crypto_ctx.pckc_hot_hint = 0;
g_process_count++;
return p;
}
}
return NULL;
}
void utsm_process_free(utsm_process_context *process) {
if (!process) return;
process->state = UTSM_PROC_UNUSED;
process->cap_table.count = 0;
process->capability_count = 0;
if (g_process_count > 0) g_process_count--;
}
utsm_process_context *utsm_process_get(u32 slot) {
if (slot >= UTSM_MAX_PROCESSES) return NULL;
return &g_process_table[slot];
}
/* ---- Capability 操作 ---- */
int utsm_process_grant_cap(utsm_process_context *process, utsm_capability cap) {
if (!process || process->cap_table.count >= UTSM_MAX_CAPABILITIES) {
return -1;
}
u32 idx = process->cap_table.count;
process->cap_table.caps[idx] = cap;
process->cap_table.count++;
process->capability_count = process->cap_table.count;
process->cap_table.generation++;
return 0;
}
int utsm_process_revoke_cap(utsm_process_context *process, u32 cap_index) {
if (!process || cap_index >= process->cap_table.count) {
return -1;
}
/* 将最后一个 capability 移到被删除的位置 */
u32 last = process->cap_table.count - 1;
if (cap_index != last) {
process->cap_table.caps[cap_index] = process->cap_table.caps[last];
}
process->cap_table.count--;
process->capability_count = process->cap_table.count;
process->cap_table.generation++;
return 0;
}
utsm_capability *utsm_process_get_cap(utsm_process_context *process, u32 cap_index) {
if (!process || cap_index >= process->cap_table.count) {
return NULL;
}
return &process->cap_table.caps[cap_index];
}
/* ---- Crypto context 操作 ---- */
void utsm_process_advance_epoch(utsm_process_context *process) {
if (!process) return;
process->crypto_epoch++;
process->crypto_ctx.crypto_epoch = process->crypto_epoch;
/* 清空 PCKC 热提示(epoch 变化后旧缓存无效) */
process->crypto_ctx.pckc_hot_hint = 0;
process->hot_segment_slot = 0;
} }
+294 -15
View File
@@ -1,37 +1,316 @@
/* drr_core.c — DRR Dedicated Recovery Root 核心实现
*
* 替换原 drr_stub.c,提供完整的:
* - Emergency Pool(独立于普通堆分配器)
* - Recovery Log(write intent/commit 追踪)
* - Checkpoint A/B 双槽(CRC 校验、原子 active slot 切换)
* - Root key 管理
* - Heartbeat
*/
#include <utsm/drr.h> #include <utsm/drr.h>
#include <utsm/log.h> #include <utsm/log.h>
static const u64 g_root_key[4] = { /* ---- 内部内存操作(不依赖 libc) ---- */
0x4452525554534d31ULL, static void drr_memzero(void *dst, u64 n) {
0xfeedfacecafebeefULL, u8 *d = (u8 *)dst;
0x0123456789abcdefULL, while (n--) *d++ = 0;
0xfedcba9876543210ULL }
static void drr_memcpy(void *dst, const void *src, u64 n) {
u8 *d = (u8 *)dst;
const u8 *s = (const u8 *)src;
while (n--) *d++ = *s++;
}
/* ---- CRC64-ECMA ---- */
static const u64 crc64_table[256] = {
0x0000000000000000ULL, 0x42F0E1EBA5EA001AULL, 0x85E1C3D753D46D34ULL, 0xC711223CF63E6D2EULL,
0x493366450E42ECDFULL, 0x0BC387AEABA8ECC5ULL, 0xCCD2A5925D9681EBULL, 0x8E224479F87C81F1ULL,
0x9266CC8A1C85D9BEULL, 0xD0962D61B96FD9A4ULL, 0x17870F5D4F51B48AULL, 0x5577EEB6EABBB490ULL,
0xDB55AACF12C73561ULL, 0x99A54B24B72D357BULL, 0x5EB4691841135855ULL, 0x1C4488F3E4F9584FULL,
0x2644C6D0383BF54AULL, 0x64B4273B9DD1F550ULL, 0xA3A505076BEF987EULL, 0xE155E4ECCE059864ULL,
0x6F77A09536791995ULL, 0x2D87417E9393198FULL, 0xEA96634265AD74A1ULL, 0xA86682A9C04774BBULL,
0xB4220A5A24BE2CF4ULL, 0xF6D2EBB181542CEEULL, 0x31C3C98D776A41C0ULL, 0x73332866D28041DAULL,
0xFD116C1F2AFCC02BULL, 0xBFE18DF48F16C031ULL, 0x78F0AFC87928AD1FULL, 0x3A004E23DCC2AD05ULL,
0x4C898D607077A834ULL, 0x0E796C8BD19DA82EULL, 0xC9684EB723A3C500ULL, 0x8B98AF5C8649C51AULL,
0x05BAEB257E3544EBULL, 0x474A0ACEDBDF44F1ULL, 0x805B28F22DE129DFULL, 0xC2ABC919880B29C5ULL,
0xDEEF41EA6CF2718AULL, 0x9C1FA001C9187190ULL, 0x5B0E823D3F261CBEULL, 0x19FE63D69ACC1CA4ULL,
0x97DC27AF62B09D55ULL, 0xD52CC644C75A9D4FULL, 0x123DE4783164F061ULL, 0x50CD0593948EF07BULL,
0x6ACD4BB0484C5D7EULL, 0x283DAA5BECA65D64ULL, 0xEF2C88671B98304AULL, 0xADDC698CBE723050ULL,
0x23FE2DF5460EB1A1ULL, 0x610ECC1EE3E4B1BBULL, 0xA61FEE2215DADC95ULL, 0xE4EF0FC9B030DC8FULL,
0xF8AB873A54C984C0ULL, 0xBA5B66D1F12384DAULL, 0x7D4A44ED071DE9F4ULL, 0x3FBAA506A2F7E9EEULL,
0xB198E17F5A8B681FULL, 0xF3680094FF616805ULL, 0x347922A8095F052BULL, 0x7689C343ACB50531ULL,
0x98131AE0E0ED2F68ULL, 0xDAE3FB0B45072F72ULL, 0x1DF2D937B339425CULL, 0x5F0238DC16D34246ULL,
0xD1207CA5EEAFC3B7ULL, 0x93D09D4E4B45C3ADULL, 0x54C1B172BD7BAE83ULL, 0x163150991891AE99ULL,
0x0A75D66AFC68F6D6ULL, 0x488537815982F6CCULL, 0x8F9415BDAFBC9BE2ULL, 0xCD64F4560A569BF8ULL,
0x4346B02FF22A1A09ULL, 0x01B651C457C01A13ULL, 0xC6A773F8A1FE773DULL, 0x8457921304147727ULL,
0xBE57DC30D8D6DA22ULL, 0xFCA73DDB7D3CDA38ULL, 0x3BB61FE78B02B716ULL, 0x7946FE0C2EE8B70CULL,
0xF764BA75D69436FDULL, 0xB5945B9E737E36E7ULL, 0x728579A285405BC9ULL, 0x3075984920AA5BD3ULL,
0x2C3110BAC453039CULL, 0x6EC1F15161B90386ULL, 0xA9D0D36D97876EA8ULL, 0xEB203286326D6EB2ULL,
0x650276FFCA11EF43ULL, 0x27F297146FFBEF59ULL, 0xE0E3B52899C58277ULL, 0xA21354C33C2F826DULL,
0xD49A9780909A875CULL, 0x966A766B35708746ULL, 0x517B5457C34EEA68ULL, 0x138BB5BC66A4EA72ULL,
0x9DA9F1C59ED86B83ULL, 0xDF59102E3B326B99ULL, 0x18483212CD0C06B7ULL, 0x5AB8D3F968E606ADULL,
0x46FC5B0A8C1F5EE2ULL, 0x040CBA4129F55EF8ULL, 0xC31D98DDDFCB33D6ULL, 0x81ED79367A2133CCULL,
0x0FCF3D4F825DB23DULL, 0x4D3FDCA427B7B227ULL, 0x8A2EFE98D189DF09ULL, 0xC8DE1F737463DF13ULL,
0xF2DE5150A8A17216ULL, 0xB02EB0BB0D4B720CULL, 0x773F9287FB751F22ULL, 0x35CF736C5E9F1F38ULL,
0xBBED3715A6E39EC9ULL, 0xF91DD6FE03099ED3ULL, 0x3E0CF4C2F537F3FDULL, 0x7CFC152950DDF3E7ULL,
0x60B89DDAB424ABA8ULL, 0x22487C3111CEABB2ULL, 0xE5595E0DE7F0C69CULL, 0xA7A9BFE6421AC686ULL,
0x298BFB9FBA664777ULL, 0x6B7B1A741F8C476DULL, 0xAC6A3848E9B22A43ULL, 0xEE9AD9A34C582A59ULL,
}; };
void drr_stub_init(void) { u64 drr_crc64(const void *data, u64 len) {
const u8 *p = (const u8 *)data;
u64 crc = 0xFFFFFFFFFFFFFFFFULL;
for (u64 i = 0; i < len; i++) {
crc = crc64_table[(crc ^ p[i]) & 0xFF] ^ (crc >> 8);
}
return crc ^ 0xFFFFFFFFFFFFFFFFULL;
}
/* ---- 全局 DRR 状态 ---- */
static drr_state g_drr;
/* ---- Root key(最终应从安全启动链获取) ---- */
static const u64 g_root_key[4] = {
0x4452525554534D31ULL, /* "DRRUTSM1" */
0xFEEDFACECAFEF00DULL,
0x0123456789ABCDEFULL,
0xFEDCBA9876543210ULL
};
/* ---- Emergency Pool 内部分配 ---- */
static void *pool_alloc_internal(u64 size, u64 alignment) {
u64 current = (u64)&g_drr.pool[0] + g_drr.pool_offset;
u64 aligned = utsm_align_up_u64(current, alignment);
u64 new_offset = (aligned - (u64)&g_drr.pool[0]) + size;
if (new_offset > g_drr.pool_limit) {
return NULL;
}
g_drr.pool_offset = new_offset;
return (void *)aligned;
}
void *drr_emergency_alloc(u64 size, u64 alignment) {
if (!g_drr.initialized) return NULL;
return pool_alloc_internal(size, alignment);
}
void drr_emergency_reset(void) {
if (!g_drr.initialized) return;
/* 保留已分配的子缓冲区,仅重置池偏移到子缓冲区之后 */
/* 注意:此函数仅在 recovery 路径紧急情况下使用 */
g_drr.pool_offset = 0;
/* 重新分配子缓冲区 */
drr_init();
}
/* ---- 初始化 ---- */
void drr_init(void) {
log_info("[DRR] init begin");
drr_memzero(&g_drr, sizeof(g_drr));
g_drr.pool_offset = 0;
g_drr.pool_limit = DRR_EMERGENCY_POOL_SIZE;
g_drr.root_key = g_root_key;
g_drr.recovery_generation = 1;
g_drr.heartbeat_interval = 1000;
g_drr.active_slot = 0; /* A */
/* 分配 Emergency Pool 子缓冲区 */
g_drr.stack_base = (u8 *)pool_alloc_internal(DRR_EMERGENCY_STACK_SIZE, 64);
g_drr.stack_size = DRR_EMERGENCY_STACK_SIZE;
g_drr.stack_ptr = DRR_EMERGENCY_STACK_SIZE; /* 栈从顶部向下增长 */
g_drr.log_base = (drr_log_entry *)pool_alloc_internal(DRR_RECOVERY_LOG_SIZE, 64);
g_drr.log_capacity = DRR_RECOVERY_LOG_SIZE / sizeof(drr_log_entry);
g_drr.log_count = 0;
g_drr.ckpt_meta_buf = (drr_checkpoint_meta *)pool_alloc_internal(DRR_CKPT_META_BUF_SIZE, 64);
g_drr.crash_buf = (u8 *)pool_alloc_internal(DRR_CRASH_BUF_SIZE, 64);
g_drr.crash_buf_size = DRR_CRASH_BUF_SIZE;
g_drr.meta_slab = pool_alloc_internal(DRR_META_SLAB_SIZE, 64);
g_drr.meta_slab_offset = 0;
/* 初始化 A/B slot */
drr_memzero(&g_drr.ckpt_a, sizeof(drr_checkpoint_meta));
drr_memzero(&g_drr.ckpt_b, sizeof(drr_checkpoint_meta));
g_drr.ckpt_a.magic = DRR_CKPT_MAGIC;
g_drr.ckpt_a.version = DRR_CKPT_VERSION;
g_drr.ckpt_a.active = 1;
g_drr.ckpt_b.magic = DRR_CKPT_MAGIC;
g_drr.ckpt_b.version = DRR_CKPT_VERSION;
g_drr.initialized = 1;
log_info("[DRR] init ok");
} }
const u64 *drr_get_root_key(void) { const u64 *drr_get_root_key(void) {
return g_root_key; return g_drr.root_key;
} }
u64 drr_recovery_generation(void) { u64 drr_recovery_generation(void) {
return 1; return g_drr.recovery_generation;
} }
/* ---- Fault 报告 ---- */
void drr_report_fault(const char *reason) { void drr_report_fault(const char *reason) {
log_error("[DRR] fault reported"); if (!g_drr.initialized) return;
log_error("[DRR] FAULT: ");
log_error(reason); log_error(reason);
/* 记录到 crash buffer */
if (g_drr.crash_buf && g_drr.crash_buf_size > 0) {
/* 简单记录:写入 reason 的前 N 字节 */
u64 i = 0;
const char *s = reason;
while (*s && i < g_drr.crash_buf_size - 1) {
g_drr.crash_buf[i++] = (u8)*s++;
}
g_drr.crash_buf[i] = 0;
}
/* 增加 recovery generation */
g_drr.recovery_generation++;
} }
/* ---- Recovery Log ---- */
void drr_log_write_intent(u32 segment_slot, u64 offset, u64 len) { void drr_log_write_intent(u32 segment_slot, u64 offset, u64 len) {
UTSM_UNUSED(segment_slot); if (!g_drr.initialized || !g_drr.log_base) return;
UTSM_UNUSED(offset); if (g_drr.log_count >= g_drr.log_capacity) {
UTSM_UNUSED(len); /* 日志满,最简单的策略:丢弃最早的条目 */
for (u32 i = 1; i < g_drr.log_count; i++) {
g_drr.log_base[i - 1] = g_drr.log_base[i];
}
g_drr.log_count--;
}
drr_log_entry *e = &g_drr.log_base[g_drr.log_count++];
e->segment_slot = segment_slot;
e->flags = DRR_LOG_FLAG_INTENT;
e->offset = offset;
e->length = len;
e->timestamp = g_drr.heartbeat_counter;
} }
void drr_log_write_commit(u32 segment_slot, u64 offset, u64 len) { void drr_log_write_commit(u32 segment_slot, u64 offset, u64 len) {
UTSM_UNUSED(segment_slot); if (!g_drr.initialized || !g_drr.log_base) return;
UTSM_UNUSED(offset); if (g_drr.log_count >= g_drr.log_capacity) {
UTSM_UNUSED(len); for (u32 i = 1; i < g_drr.log_count; i++) {
g_drr.log_base[i - 1] = g_drr.log_base[i];
}
g_drr.log_count--;
}
drr_log_entry *e = &g_drr.log_base[g_drr.log_count++];
e->segment_slot = segment_slot;
e->flags = DRR_LOG_FLAG_COMMIT;
e->offset = offset;
e->length = len;
e->timestamp = g_drr.heartbeat_counter;
}
/* ---- Checkpoint ---- */
int drr_checkpoint_begin(drr_checkpoint_type type) {
if (!g_drr.initialized) return -1;
/* 选择 inactive slot */
drr_checkpoint_meta *target;
if (g_drr.active_slot == 0) {
target = &g_drr.ckpt_b;
} else {
target = &g_drr.ckpt_a;
}
target->magic = DRR_CKPT_MAGIC;
target->version = DRR_CKPT_VERSION;
target->active = 0; /* 写入时为 inactive */
target->type = type;
target->global_epoch = g_drr.recovery_generation;
target->dirty_page_count = 0;
target->mac_root = 0;
target->segment_count = 0;
target->crc = 0;
return 0;
}
int drr_checkpoint_finish(void) {
if (!g_drr.initialized) return -1;
/* 写入 inactive slot 的 CRC */
drr_checkpoint_meta *target;
if (g_drr.active_slot == 0) {
target = &g_drr.ckpt_b;
} else {
target = &g_drr.ckpt_a;
}
/* 计算 CRC(不含 crc 字段本身) */
target->crc = drr_crc64(target,
sizeof(drr_checkpoint_meta) - sizeof(u64));
/* 原子切换 active slot */
target->active = 1;
/* 旧 slot 标记 inactive */
if (g_drr.active_slot == 0) {
g_drr.ckpt_a.active = 0;
g_drr.active_slot = 1;
} else {
g_drr.ckpt_b.active = 0;
g_drr.active_slot = 0;
}
/* 增加 recovery generation */
g_drr.recovery_generation++;
/* 清空 recovery log */
g_drr.log_count = 0;
return 0;
}
int drr_checkpoint_recover(drr_checkpoint_meta *out_meta) {
if (!g_drr.initialized || !out_meta) return -1;
/* 尝试 active slot */
drr_checkpoint_meta *active = (g_drr.active_slot == 0) ?
&g_drr.ckpt_a : &g_drr.ckpt_b;
drr_checkpoint_meta *fallback = (g_drr.active_slot == 0) ?
&g_drr.ckpt_b : &g_drr.ckpt_a;
/* 验证 active slot */
if (active->magic == DRR_CKPT_MAGIC && active->active == 1) {
u64 expected_crc = active->crc;
active->crc = 0;
u64 actual_crc = drr_crc64(active,
sizeof(drr_checkpoint_meta) - sizeof(u64));
active->crc = expected_crc;
if (actual_crc == expected_crc) {
drr_memcpy(out_meta, active, sizeof(drr_checkpoint_meta));
return 0;
}
}
/* active 无效,尝试 fallback */
if (fallback->magic == DRR_CKPT_MAGIC && fallback->active == 1) {
u64 expected_crc = fallback->crc;
fallback->crc = 0;
u64 actual_crc = drr_crc64(fallback,
sizeof(drr_checkpoint_meta) - sizeof(u64));
fallback->crc = expected_crc;
if (actual_crc == expected_crc) {
drr_memcpy(out_meta, fallback, sizeof(drr_checkpoint_meta));
return 0;
}
}
/* 双槽都无效 */
return -2;
}
/* ---- Heartbeat ---- */
void drr_heartbeat_tick(void) {
if (!g_drr.initialized) return;
g_drr.heartbeat_counter++;
}
u64 drr_heartbeat_get(void) {
return g_drr.heartbeat_counter;
} }
+708
View File
@@ -0,0 +1,708 @@
/* deshab_ui.h — Deshab 视觉风格系统
*
* "Sealed Arc" 设计语言:以 UTSM 封缄内存为灵感的统一视觉体系。
*
* 核心设计隐喻:
* Arc(弧) — 封缄/解封的视觉表达,圆角、弧形边框、弧线动画
* Seal(封) — 密封容器的安全感,卡片容器、发光边缘、透明覆盖
* Flow(流) — 数据流的连续性,渐变背景、平滑过渡、流式布局
*
* 设计令牌分层:
* L0 — 原始色值(Deshab Palette)
* L1 — 语义色值(Semantic Colors,引用 L0)
* L2 — 组件令牌(Component Tokens,引用 L1)
*
* 所有 UI 组件必须通过本头文件获取视觉参数,禁止硬编码颜色/尺寸。
*/
#ifndef DESHAB_UI_H
#define DESHAB_UI_H
/* ===================================================================
* 基础类型(自包含,不依赖 utsm/types.h 以便 DSK/Shell 独立使用)
* =================================================================== */
typedef unsigned char du_u8;
typedef unsigned short du_u16;
typedef unsigned int du_u32;
typedef unsigned long long du_u64;
typedef long long du_i64;
/* ===================================================================
* L0 — Deshab 原始色板 (Deshab Palette)
*
* 色彩命名规则:{色调}_{明度}
* 格式:0xAARRGGBB(与 framebuffer 一致,小端 x86 上 BB 置低位)
* =================================================================== */
/* --- 主色调:深海蓝 (Abyss Blue) --- */
#define DP_ABYSS_900 0xFF0A1428u /* 最深:终端背景、底色 */
#define DP_ABYSS_800 0xFF0F1E38u /* 深层:暗卡片背景 */
#define DP_ABYSS_700 0xFF162848u /* 暗面:悬停态背景 */
#define DP_ABYSS_600 0xFF1E3868u /* 中暗:输入框背景 */
#define DP_ABYSS_500 0xFF284888u /* 中间:次级强调 */
#define DP_ABYSS_400 0xFF3868B0u /* 中亮:默认强调 */
#define DP_ABYSS_300 0xFF5088D0u /* 亮面:链接色 */
#define DP_ABYSS_200 0xFF78A8E8u /* 高亮:焦点指示 */
#define DP_ABYSS_100 0xFFA8CCF4u /* 最亮:浅色辅助 */
/* --- 强调色:封缄青 (Seal Cyan) — 主要操作/交互 --- */
#define DP_SEAL_900 0xFF004858u
#define DP_SEAL_700 0xFF007898u
#define DP_SEAL_500 0xFF00A8CCu /* 标准强调:按钮、选中态 */
#define DP_SEAL_300 0xFF44CCF0u /* 高亮强调:焦点环 */
#define DP_SEAL_100 0xFF99E8FFu /* 亮强调:发光边缘 */
/* --- 辅助色:弧光紫 (Arc Violet) — 次级/装饰 --- */
#define DP_ARC_900 0xFF2A1040u
#define DP_ARC_700 0xFF5A2080u
#define DP_ARC_500 0xFF8844CCu /* 装饰强调:渐变终点、图标 */
#define DP_ARC_300 0xFFB888E8u
#define DP_ARC_100 0xFFE0C8F8u
/* --- 中性色 (Neutral) --- */
#define DP_NEUTRAL_0 0xFF000000u /* 纯黑 */
#define DP_NEUTRAL_50 0xFF181820u /* 接近黑 */
#define DP_NEUTRAL_100 0xFF2A2A38u /* 暗灰 */
#define DP_NEUTRAL_200 0xFF484860u /* 中暗灰 */
#define DP_NEUTRAL_300 0xFF6A6A88u /* 中灰 */
#define DP_NEUTRAL_400 0xFF9090A8u /* 中亮灰 */
#define DP_NEUTRAL_500 0xFFB0B0C0u /* 标准灰 */
#define DP_NEUTRAL_600 0xFFD0D0DCu /* 亮灰 */
#define DP_NEUTRAL_700 0xFFE8E8F0u /* 浅灰 */
#define DP_NEUTRAL_800 0xFFF0F0F8u /* 接近白 */
#define DP_NEUTRAL_900 0xFFFFFFFFu /* 纯白 */
/* --- 语义辅助色 --- */
#define DP_SUCCESS 0xFF40C880u /* 成功:绿色 */
#define DP_WARNING 0xFFF0A030u /* 警告:琥珀色 */
#define DP_ERROR 0xFFFF4466u /* 错误:红色 */
#define DP_INFO 0xFF4499FFu /* 信息:蓝色 */
/* --- 渐变色 — DSK 加载器 / FirstInit 背景专用 --- */
#define DP_GRAD_TOP 0xFFC8E0F0u /* 淡蓝顶 */
#define DP_GRAD_MID 0xFF8888B0u /* 紫灰中 */
#define DP_GRAD_BOT 0xFF49306Fu /* 深紫底 */
/* --- 欢迎页专用 --- */
#define DP_WELCOME_FG 0xFFE8F2FCu /* 近白浅蓝(欢迎文字) */
/* ===================================================================
* L1 — 语义色值 (Semantic Colors)
*
* 暗色主题(Dark Theme)— Shell / 终端
* 亮色主题(Light Theme)— FirstInit / DSK
* =================================================================== */
/* --- 暗色主题 --- */
#define DS_DARK_BG_PRIMARY DP_ABYSS_900 /* 主背景 */
#define DS_DARK_BG_SECONDARY DP_ABYSS_800 /* 卡片/面板背景 */
#define DS_DARK_BG_TERTIARY DP_ABYSS_700 /* 悬停/输入框 */
#define DS_DARK_BG_INSET DP_ABYSS_600 /* 凹陷区域 */
#define DS_DARK_TEXT_PRIMARY DP_NEUTRAL_700 /* 主文字 */
#define DS_DARK_TEXT_SECONDARY DP_NEUTRAL_500 /* 次级文字 */
#define DS_DARK_TEXT_DIM DP_NEUTRAL_300 /* 注释/禁用 */
#define DS_DARK_TEXT_INVERT DP_ABYSS_900 /* 反色文字(亮背景上) */
#define DS_DARK_ACCENT DP_SEAL_500 /* 主强调 */
#define DS_DARK_ACCENT_LIGHT DP_SEAL_300 /* 高亮强调 */
#define DS_DARK_ACCENT_GLOW DP_SEAL_100 /* 发光 */
#define DS_DARK_DECORATIVE DP_ARC_500 /* 装饰色 */
#define DS_DARK_DECORATIVE_L DP_ARC_300 /* 浅装饰 */
#define DS_DARK_BORDER DP_ABYSS_500 /* 边框 */
#define DS_DARK_BORDER_FOCUS DP_SEAL_300 /* 焦点边框 */
#define DS_DARK_DIVIDER DP_ABYSS_600 /* 分隔线 */
#define DS_DARK_CURSOR DP_SEAL_500 /* 光标 */
#define DS_DARK_PROMPT DP_SEAL_300 /* 提示符 */
#define DS_DARK_SELECTION_BG DP_SEAL_900 /* 选中背景 */
/* --- 亮色主题 --- */
#define DS_LIGHT_BG_PRIMARY DP_NEUTRAL_900 /* 主背景(白) */
#define DS_LIGHT_BG_SECONDARY DP_NEUTRAL_700 /* 卡片背景 */
#define DS_LIGHT_BG_TERTIARY DP_NEUTRAL_600 /* 悬停 */
#define DS_LIGHT_BG_INSET DP_NEUTRAL_500 /* 输入框 */
#define DS_LIGHT_TEXT_PRIMARY DP_ABYSS_900 /* 主文字 */
#define DS_LIGHT_TEXT_SECONDARY DP_ABYSS_500 /* 次级 */
#define DS_LIGHT_TEXT_DIM DP_NEUTRAL_300 /* 注释 */
#define DS_LIGHT_TEXT_INVERT DP_NEUTRAL_900 /* 反色 */
#define DS_LIGHT_ACCENT DP_SEAL_700 /* 主强调(亮色上更深) */
#define DS_LIGHT_ACCENT_LIGHT DP_SEAL_500
#define DS_LIGHT_DECORATIVE DP_ARC_700
#define DS_LIGHT_BORDER DP_NEUTRAL_400
#define DS_LIGHT_BORDER_FOCUS DP_SEAL_500
/* ===================================================================
* 排版规范 (Typography)
* =================================================================== */
/* 字体尺寸族 */
#define DU_FONT_SIZE_XS 10u /* 辅助标注 */
#define DU_FONT_SIZE_SM 14u /* 次要说明 */
#define DU_FONT_SIZE_MD 18u /* 正文(Consolas 18px) */
#define DU_FONT_SIZE_LG 24u /* 小标题 */
#define DU_FONT_SIZE_XL 32u /* 标题 */
#define DU_FONT_SIZE_2XL 48u /* 大标题 / 欢迎文字 */
/* ASCII 位图字体参数(与 ascii_bitmaps.c 对应) */
#define DU_ASCII_CELL_W 11u
#define DU_ASCII_CELL_H 18u
#define DU_ASCII_STEP 12u /* 字符间距(含 1px gap) */
#define DU_ASCII_LINE_H 20u /* 行高(含 2px 行间距) */
/* ===================================================================
* 间距体系 (Spacing)
* =================================================================== */
#define DU_SPACE_XXS 2u
#define DU_SPACE_XS 4u
#define DU_SPACE_SM 8u
#define DU_SPACE_MD 16u
#define DU_SPACE_LG 24u
#define DU_SPACE_XL 32u
#define DU_SPACE_2XL 48u
#define DU_SPACE_3XL 64u
/* ===================================================================
* 圆角体系 (Border Radius) — "Sealed Arc" 签名元素
* =================================================================== */
#define DU_RADIUS_NONE 0u
#define DU_RADIUS_SM 4u /* 小按钮、标签 */
#define DU_RADIUS_MD 8u /* 卡片、输入框 */
#define DU_RADIUS_LG 12u /* 大卡片、面板 */
#define DU_RADIUS_XL 16u /* 模态框 */
#define DU_RADIUS_FULL 9999u /* 完全圆角(药丸形) */
/* ===================================================================
* 边框体系 (Border)
* =================================================================== */
#define DU_BORDER_THIN 1u
#define DU_BORDER_STD 2u
#define DU_BORDER_THICK 3u
/* ===================================================================
* 动画参数 (Animation)
* =================================================================== */
/* 帧延迟(busy-wait 循环次数 ≈ 毫秒级,需根据 CPU 速度校准) */
#define DU_ANIM_DELAY_SLOW 200000u /* 慢速过渡 */
#define DU_ANIM_DELAY_NORMAL 100000u /* 标准动画 */
#define DU_ANIM_DELAY_FAST 40000u /* 快速反馈 */
/* 弧线动画参数(DSK spinner 专用) */
#define DU_SPINNER_RADIUS 48u
#define DU_SPINNER_THICKNESS 4u
#define DU_SPINNER_ARC_GAP 82u /* 每弧宽度(度) */
#define DU_SPINNER_ARC_COUNT 4u
#define DU_SPINNER_SPEED 2u /* 每帧旋转度数 */
#define DU_SPINNER_SPRITE 128u /* 精灵缓冲边长 */
/* 渐变动画步进 */
#define DU_FADE_STEPS 32u /* 淡入/淡出步数 */
#define DU_FADE_STEP_DELAY 8000u /* 每步延迟 */
/* ===================================================================
* 组件尺寸 (Component Sizes)
* =================================================================== */
#define DU_BUTTON_HEIGHT 36u
#define DU_INPUT_HEIGHT 32u
#define DU_CARD_PADDING_X 24u
#define DU_CARD_PADDING_Y 16u
#define DU_CARD_MIN_WIDTH 200u
#define DU_CARD_MAX_WIDTH 480u
#define DU_CURSOR_SIZE 24u /* 鼠标光标边长 */
#define DU_NOTIFICATION_H 40u
/* ===================================================================
* 渲染上下文 (Render Context)
*
* 所有绘制函数通过此上下文访问 framebuffer,避免全局状态。
* =================================================================== */
typedef struct {
du_u32 *fb; /* framebuffer 基址 */
du_u64 width; /* 像素宽 */
du_u64 height; /* 像素高 */
du_u64 pitch; /* 字节行宽 */
} du_context;
/* 初始化渲染上下文 */
static inline void du_context_init(du_context *ctx, du_u64 fb_addr,
du_u64 w, du_u64 h, du_u64 pitch) {
ctx->fb = (du_u32 *)(du_u64)fb_addr;
ctx->width = w;
ctx->height = h;
ctx->pitch = pitch;
}
/* ===================================================================
* L2 — 绘制原语 (Rendering Primitives)
* =================================================================== */
/* 颜色混合:alpha 范围 0–255 */
static inline du_u32 du_blend(du_u32 bg, du_u32 fg, du_u32 alpha) {
du_u32 na = 256 - alpha;
du_u32 r = ((bg & 0xFF) * na + (fg & 0xFF) * alpha) >> 8;
du_u32 g = (((bg >> 8) & 0xFF) * na + ((fg >> 8) & 0xFF) * alpha) >> 8;
du_u32 b = (((bg >> 16) & 0xFF) * na + ((fg >> 16) & 0xFF) * alpha) >> 8;
return 0xFF000000u | (b << 16) | (g << 8) | r;
}
/* 线性插值两色 */
static inline du_u32 du_lerp_color(du_u32 c1, du_u32 c2, du_u32 t) {
return du_blend(c1, c2, t);
}
/* 渐变背景:顶色 → 底色,按 Y 坐标插值 */
static inline du_u32 du_gradient_v(du_u32 top_color, du_u32 bot_color,
du_i64 y, du_i64 total_h) {
if (total_h <= 1) return top_color;
if (y < 0) y = 0;
if (y >= total_h) y = total_h - 1;
du_u32 alpha = (du_u32)(((du_u64)y * 255ULL) / ((du_u64)total_h - 1));
return du_blend(top_color, bot_color, alpha);
}
/* 单像素写(带边界检查) */
static inline void du_pixel(du_context *ctx, du_i64 x, du_i64 y, du_u32 color) {
if (x < 0 || (du_u64)x >= ctx->width || y < 0 || (du_u64)y >= ctx->height) return;
((du_u32 *)((du_u8 *)ctx->fb + (du_u64)y * ctx->pitch))[(du_u64)x] = color;
}
/* 读取像素(带边界检查) */
static inline du_u32 du_pixel_read(du_context *ctx, du_i64 x, du_i64 y) {
if (x < 0 || (du_u64)x >= ctx->width || y < 0 || (du_u64)y >= ctx->height) return 0;
return ((du_u32 *)((du_u8 *)ctx->fb + (du_u64)y * ctx->pitch))[(du_u64)x];
}
/* 矩形填充 */
static inline void du_fill_rect(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, du_i64 h, du_u32 color) {
for (du_i64 r = 0; r < h; r++) {
du_i64 yy = y + r;
if (yy < 0 || (du_u64)yy >= ctx->height) continue;
du_u32 *line = (du_u32 *)((du_u8 *)ctx->fb + (du_u64)yy * ctx->pitch);
for (du_i64 c = 0; c < w; c++) {
du_i64 xx = x + c;
if (xx < 0 || (du_u64)xx >= ctx->width) continue;
line[(du_u64)xx] = color;
}
}
}
/* 渐变矩形填充(垂直渐变) */
static inline void du_fill_rect_gradient(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, du_i64 h,
du_u32 top_color, du_u32 bot_color) {
for (du_i64 r = 0; r < h; r++) {
du_i64 yy = y + r;
if (yy < 0 || (du_u64)yy >= ctx->height) continue;
du_u32 color = du_gradient_v(top_color, bot_color, r, h);
du_u32 *line = (du_u32 *)((du_u8 *)ctx->fb + (du_u64)yy * ctx->pitch);
for (du_i64 c = 0; c < w; c++) {
du_i64 xx = x + c;
if (xx < 0 || (du_u64)xx >= ctx->width) continue;
line[(du_u64)xx] = color;
}
}
}
/* 全屏渐变背景 */
static inline void du_fill_bg_gradient(du_context *ctx,
du_u32 top_color, du_u32 bot_color) {
du_fill_rect_gradient(ctx, 0, 0, (du_i64)ctx->width, (du_i64)ctx->height,
top_color, bot_color);
}
/* 全屏纯色背景 */
static inline void du_fill_bg_solid(du_context *ctx, du_u32 color) {
du_fill_rect(ctx, 0, 0, (du_i64)ctx->width, (du_i64)ctx->height, color);
}
/* ===================================================================
* 圆角矩形 (Rounded Rectangle) — "Sealed Arc" 核心形状
* =================================================================== */
/* 绘制圆角矩形边框(单像素宽) */
static inline void du_rect_outline(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, du_i64 h,
du_u32 color, du_u32 radius) {
/* 四个角的圆弧 + 四条直线 */
du_i64 r = (du_i64)radius;
if (r > w / 2) r = w / 2;
if (r > h / 2) r = h / 2;
/* 顶线 */
for (du_i64 c = r; c < w - r; c++) {
du_pixel(ctx, x + c, y, color);
}
/* 底线 */
for (du_i64 c = r; c < w - r; c++) {
du_pixel(ctx, x + c, y + h - 1, color);
}
/* 左线 */
for (du_i64 rr = r; rr < h - r; rr++) {
du_pixel(ctx, x, y + rr, color);
}
/* 右线 */
for (du_i64 rr = r; rr < h - r; rr++) {
du_pixel(ctx, x + w - 1, y + rr, color);
}
/* 四个圆角 */
for (du_i64 dy = 0; dy <= r; dy++) {
for (du_i64 dx = 0; dx <= r; dx++) {
du_i64 d2 = dx * dx + dy * dy;
if (d2 <= r * r && d2 > (r - 1) * (r - 1)) {
/* 左上 */
du_pixel(ctx, x + r - dx, y + r - dy, color);
/* 右上 */
du_pixel(ctx, x + w - 1 - r + dx, y + r - dy, color);
/* 左下 */
du_pixel(ctx, x + r - dx, y + h - 1 - r + dy, color);
/* 右下 */
du_pixel(ctx, x + w - 1 - r + dx, y + h - 1 - r + dy, color);
}
}
}
}
/* 填充圆角矩形 */
static inline void du_fill_rounded_rect(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, du_i64 h,
du_u32 color, du_u32 radius) {
du_i64 r = (du_i64)radius;
if (r > w / 2) r = w / 2;
if (r > h / 2) r = h / 2;
/* 中间区域(不含角) */
du_fill_rect(ctx, x + r, y, w - 2 * r, h, color);
/* 左侧矩形 */
du_fill_rect(ctx, x, y + r, r, h - 2 * r, color);
/* 右侧矩形 */
du_fill_rect(ctx, x + w - r, y + r, r, h - 2 * r, color);
/* 四个圆角填充 */
for (du_i64 dy = 0; dy < r; dy++) {
for (du_i64 dx = 0; dx < r; dx++) {
if (dx * dx + dy * dy <= r * r) {
/* 左上 */
du_pixel(ctx, x + r - 1 - dx, y + r - 1 - dy, color);
/* 右上 */
du_pixel(ctx, x + w - r + dx, y + r - 1 - dy, color);
/* 左下 */
du_pixel(ctx, x + r - 1 - dx, y + h - r + dy, color);
/* 右下 */
du_pixel(ctx, x + w - r + dx, y + h - r + dy, color);
}
}
}
}
/* 带发光效果的圆角矩形边框(模拟 UTSM 封缄发光) */
static inline void du_rect_glow(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, du_i64 h,
du_u32 glow_color, du_u32 radius,
du_i64 glow_width) {
/* 从外到内逐层降低透明度 */
for (du_i64 i = glow_width; i >= 1; i--) {
du_u32 alpha = (du_u32)(40 + (glow_width - i) * 50 / glow_width);
du_u32 color = du_blend(DP_ABYSS_900, glow_color, alpha);
du_rect_outline(ctx, x - i, y - i, w + 2 * i, h + 2 * i, color, radius + (du_u32)i);
}
/* 核心边框 */
du_rect_outline(ctx, x, y, w, h, glow_color, radius);
}
/* ===================================================================
* 分隔线 (Divider)
* =================================================================== */
static inline void du_divider_h(du_context *ctx, du_i64 x, du_i64 y,
du_i64 width, du_u32 color) {
du_fill_rect(ctx, x, y, width, 1, color);
}
static inline void du_divider_v(du_context *ctx, du_i64 x, du_i64 y,
du_i64 height, du_u32 color) {
du_fill_rect(ctx, x, y, 1, height, color);
}
/* ===================================================================
* ASCII 字符渲染(需要外部提供 g_ascii 位图数组)
*
* 使用方法:包含 ascii_bitmaps.c 后调用 du_draw_char / du_draw_string
* =================================================================== */
/* ASCII 位图数组 — 由 ascii_bitmaps.c 定义(static),在包含该 .c 的编译单元内可见 */
/* 渲染单个 ASCII 字符(灰度位图 + 前景色 alpha 混合) */
static inline void du_draw_char(du_context *ctx, char ch,
du_i64 x, du_i64 y,
du_u32 fg, du_u32 bg) {
du_u32 idx = (du_u32)(ch - ' ');
if (idx > 94) idx = 0;
const du_u8 *glyph = g_ascii[idx];
du_i64 gw = g_ascii_w;
du_i64 gh = g_ascii_h;
for (du_i64 r = 0; r < gh; r++) {
du_i64 yy = y + r;
if (yy < 0 || (du_u64)yy >= ctx->height) continue;
du_u32 *line = (du_u32 *)((du_u8 *)ctx->fb + (du_u64)yy * ctx->pitch);
for (du_i64 c = 0; c < gw; c++) {
du_u8 a = glyph[r * gw + c];
if (a == 0) continue;
du_i64 xx = x + c;
if (xx < 0 || (du_u64)xx >= ctx->width) continue;
line[(du_u64)xx] = (a == 255) ? fg : du_blend(bg, fg, a);
}
}
}
/* 渲染字符串(不换行,无自动折行) */
static inline void du_draw_string(du_context *ctx, const char *s,
du_i64 x, du_i64 y,
du_u32 fg, du_u32 bg,
du_u32 step) {
du_i64 cx = x;
while (*s) {
du_draw_char(ctx, *s, cx, y, fg, bg);
cx += (du_i64)step;
s++;
}
}
/* ===================================================================
* 组件令牌 — 按钮 (Button)
* =================================================================== */
typedef struct {
du_u32 bg_normal; /* 正常背景 */
du_u32 bg_hover; /* 悬停背景 */
du_u32 bg_active; /* 按下背景 */
du_u32 fg; /* 文字色 */
du_u32 border; /* 边框色 */
du_u32 border_focus; /* 焦点边框色 */
du_u32 radius; /* 圆角半径 */
du_u32 height; /* 高度 */
du_u32 padding_x; /* 水平内边距 */
} du_button_style;
/* 暗色主题按钮样式 */
#define DU_BTN_DARK_PRIMARY { \
DS_DARK_ACCENT, DS_DARK_ACCENT_LIGHT, DP_SEAL_700, \
DP_NEUTRAL_900, DP_SEAL_300, DP_SEAL_100, \
DU_RADIUS_MD, DU_BUTTON_HEIGHT, DU_SPACE_LG \
}
#define DU_BTN_DARK_SECONDARY { \
DS_DARK_BG_TERTIARY, DS_DARK_BG_INSET, DS_DARK_BG_INSET, \
DS_DARK_TEXT_PRIMARY, DS_DARK_BORDER, DS_DARK_BORDER_FOCUS, \
DU_RADIUS_MD, DU_BUTTON_HEIGHT, DU_SPACE_LG \
}
/* ===================================================================
* 组件令牌 — 卡片 (Card)
* =================================================================== */
typedef struct {
du_u32 bg; /* 卡片背景 */
du_u32 border; /* 边框色 */
du_u32 border_focus; /* 焦点边框色 */
du_u32 shadow_color; /* 阴影色 */
du_u32 radius; /* 圆角半径 */
du_u32 padding_x; /* 水平内边距 */
du_u32 padding_y; /* 垂直内边距 */
du_u32 border_width; /* 边框宽度 */
} du_card_style;
#define DU_CARD_DARK { \
DS_DARK_BG_SECONDARY, DS_DARK_BORDER, DS_DARK_BORDER_FOCUS, \
DP_ABYSS_900, DU_RADIUS_LG, DU_CARD_PADDING_X, DU_CARD_PADDING_Y, DU_BORDER_STD \
}
#define DU_CARD_LIGHT { \
DS_LIGHT_BG_SECONDARY, DS_LIGHT_BORDER, DS_LIGHT_BORDER_FOCUS, \
DP_NEUTRAL_300, DU_RADIUS_LG, DU_CARD_PADDING_X, DU_CARD_PADDING_Y, DU_BORDER_STD \
}
/* ===================================================================
* 组件令牌 — 输入框 (Input)
* =================================================================== */
typedef struct {
du_u32 bg;
du_u32 bg_focus;
du_u32 fg;
du_u32 placeholder;
du_u32 border;
du_u32 border_focus;
du_u32 cursor;
du_u32 radius;
du_u32 height;
} du_input_style;
#define DU_INPUT_DARK { \
DS_DARK_BG_INSET, DS_DARK_BG_TERTIARY, DS_DARK_TEXT_PRIMARY, \
DS_DARK_TEXT_DIM, DS_DARK_BORDER, DS_DARK_BORDER_FOCUS, \
DS_DARK_CURSOR, DU_RADIUS_MD, DU_INPUT_HEIGHT \
}
/* ===================================================================
* 组件令牌 — 通知栏 (Notification)
* =================================================================== */
typedef enum {
DU_NOTIFY_INFO,
DU_NOTIFY_SUCCESS,
DU_NOTIFY_WARNING,
DU_NOTIFY_ERROR
} du_notify_type;
static inline du_u32 du_notify_color(du_notify_type type) {
switch (type) {
case DU_NOTIFY_SUCCESS: return DP_SUCCESS;
case DU_NOTIFY_WARNING: return DP_WARNING;
case DU_NOTIFY_ERROR: return DP_ERROR;
default: return DP_INFO;
}
}
/* ===================================================================
* 组件绘制函数 (Component Drawing)
* =================================================================== */
/* 绘制按钮 */
static inline void du_draw_button(du_context *ctx,
du_i64 x, du_i64 y, du_i64 w,
const du_button_style *style,
const char *label, int focused) {
du_u32 bg = style->bg_normal;
du_u32 border = style->border;
if (focused) {
border = style->border_focus;
}
/* 填充圆角矩形背景 */
du_fill_rounded_rect(ctx, x, y, w, (du_i64)style->height, bg, style->radius);
/* 边框 */
du_rect_outline(ctx, x, y, w, (du_i64)style->height, border, style->radius);
/* 文字居中 */
du_i64 label_len = 0;
while (label[label_len]) label_len++;
du_i64 text_w = label_len * (du_i64)DU_ASCII_STEP;
du_i64 text_x = x + (w - text_w) / 2;
du_i64 text_y = y + ((du_i64)style->height - (du_i64)DU_ASCII_CELL_H) / 2;
du_draw_string(ctx, label, text_x, text_y, style->fg, bg, DU_ASCII_STEP);
}
/* 绘制卡片 */
static inline void du_draw_card(du_context *ctx,
du_i64 x, du_i64 y, du_i64 w, du_i64 h,
const du_card_style *style, int focused) {
du_u32 border = style->border;
if (focused) {
border = style->border_focus;
/* 焦点发光效果 */
du_rect_glow(ctx, x - 2, y - 2, w + 4, h + 4,
border, style->radius + 2, 3);
}
du_fill_rounded_rect(ctx, x, y, w, h, style->bg, style->radius);
du_rect_outline(ctx, x, y, w, h, border, style->radius);
}
/* 绘制输入框 */
static inline void du_draw_input(du_context *ctx,
du_i64 x, du_i64 y, du_i64 w,
const du_input_style *style,
const char *text, int cursor_pos,
int focused) {
du_u32 bg = focused ? style->bg_focus : style->bg;
du_u32 border = focused ? style->border_focus : style->border;
du_fill_rounded_rect(ctx, x, y, w, (du_i64)style->height, bg, style->radius);
du_rect_outline(ctx, x, y, w, (du_i64)style->height, border, style->radius);
/* 文字 */
du_i64 text_x = x + (du_i64)DU_SPACE_SM;
du_i64 text_y = y + ((du_i64)style->height - (du_i64)DU_ASCII_CELL_H) / 2;
du_u32 fg = text[0] ? style->fg : style->placeholder;
du_draw_string(ctx, text, text_x, text_y, fg, bg, DU_ASCII_STEP);
/* 光标 */
if (focused && cursor_pos >= 0) {
du_i64 cx = text_x + (du_i64)cursor_pos * (du_i64)DU_ASCII_STEP;
du_fill_rect(ctx, cx, text_y, 2, (du_i64)DU_ASCII_CELL_H, style->cursor);
}
}
/* 绘制通知栏 */
static inline void du_draw_notification(du_context *ctx,
du_i64 x, du_i64 y, du_i64 w,
du_notify_type type,
const char *message) {
du_u32 accent = du_notify_color(type);
/* 半透明背景 */
du_fill_rounded_rect(ctx, x, y, w, (du_i64)DU_NOTIFICATION_H,
DP_ABYSS_800, DU_RADIUS_MD);
/* 左侧色条 */
du_fill_rect(ctx, x, y + 4, 4, (du_i64)DU_NOTIFICATION_H - 8, accent);
/* 文字 */
du_i64 text_y = y + ((du_i64)DU_NOTIFICATION_H - (du_i64)DU_ASCII_CELL_H) / 2;
du_draw_string(ctx, message, x + (du_i64)DU_SPACE_MD, text_y,
DS_DARK_TEXT_PRIMARY, DP_ABYSS_800, DU_ASCII_STEP);
}
/* 绘制进度条 */
static inline void du_draw_progress(du_context *ctx,
du_i64 x, du_i64 y, du_i64 w, du_i64 h,
du_u32 progress_0_255) {
/* 背景 */
du_fill_rounded_rect(ctx, x, y, w, h, DP_ABYSS_700, DU_RADIUS_FULL);
/* 填充 */
du_i64 fill_w = (w * (du_i64)progress_0_255) / 255;
if (fill_w > 0) {
du_fill_rounded_rect(ctx, x, y, fill_w, h, DP_SEAL_500, DU_RADIUS_FULL);
}
}
/* ===================================================================
* Spinner(加载弧) — DSK 启动加载动画
*
* 使用 "Sealed Arc" 四弧旋转设计,弧色取自色板。
* =================================================================== */
/* 默认弧色(从亮到暗的青蓝色) */
#define DU_SPINNER_ARCS { \
DP_SEAL_100, DP_SEAL_300, DP_SEAL_500, DP_SEAL_700 \
}
/* ===================================================================
* 主题切换辅助 (Theme Switch)
* =================================================================== */
typedef enum {
DU_THEME_DARK,
DU_THEME_LIGHT
} du_theme;
/* 根据主题获取对应的背景色 */
static inline du_u32 du_theme_bg(du_theme t) {
return (t == DU_THEME_DARK) ? DS_DARK_BG_PRIMARY : DS_LIGHT_BG_PRIMARY;
}
/* 根据主题获取对应的文字色 */
static inline du_u32 du_theme_text(du_theme t) {
return (t == DU_THEME_DARK) ? DS_DARK_TEXT_PRIMARY : DS_LIGHT_TEXT_PRIMARY;
}
/* 根据主题获取对应的强调色 */
static inline du_u32 du_theme_accent(du_theme t) {
return (t == DU_THEME_DARK) ? DS_DARK_ACCENT : DS_LIGHT_ACCENT;
}
#endif /* DESHAB_UI_H */
+116 -2
View File
@@ -2,12 +2,126 @@
#define UTSM_DRR_H #define UTSM_DRR_H
#include <utsm/types.h> #include <utsm/types.h>
#include <utsm/config.h>
void drr_stub_init(void); /* ===================================================================
* DRR — Dedicated Recovery Root
*
* 专用恢复根,独立于普通调度器和堆分配器。
* 负责:root key、recovery log、checkpoint A/B、Emergency Pool。
*
* 约束:
* - DRR recovery 路径禁止依赖普通堆分配器
* - 普通 OOM 不影响 DRR recovery
* - Emergency Pool 耗尽时进入 system rollback
* =================================================================== */
/* ---- Checkpoint 类型 ---- */
typedef enum {
DRR_CKPT_LIGHT = 0, /* 控制面快照 */
DRR_CKPT_DIRTY_PAGE, /* dirty ciphertext page 快照 */
DRR_CKPT_BOOT /* 启动状态 + A/B slot */
} drr_checkpoint_type;
/* ---- Recovery Log 条目 ---- */
typedef struct {
u32 segment_slot;
u32 flags; /* 0=intent, 1=commit */
u64 offset;
u64 length;
u64 timestamp; /* writer_seq snapshot */
} drr_log_entry;
#define DRR_LOG_FLAG_INTENT 0u
#define DRR_LOG_FLAG_COMMIT 1u
/* ---- Checkpoint Metadata Slot ---- */
typedef struct {
u64 magic; /* DRR_CKPT_MAGIC */
u32 version;
u32 active; /* 0=inactive, 1=active */
drr_checkpoint_type type;
u64 global_epoch;
u64 dirty_page_count;
u64 mac_root; /* MAC root hash of all sealed pages */
u64 segment_count;
u64 crc; /* CRC64 of the above */
} drr_checkpoint_meta;
#define DRR_CKPT_MAGIC 0x445252434B505421ULL /* "DRRCKPT!" */
#define DRR_CKPT_VERSION 1u
/* ---- Emergency Pool ---- */
#define DRR_EMERGENCY_POOL_SIZE (64ULL * 1024ULL) /* 64KB 独立池 */
#define DRR_EMERGENCY_STACK_SIZE 4096ULL
#define DRR_RECOVERY_LOG_SIZE 4096ULL
#define DRR_CKPT_META_BUF_SIZE 4096ULL
#define DRR_CRASH_BUF_SIZE 2048ULL
#define DRR_META_SLAB_SIZE 2048ULL
#define DRR_EMERGENCY_PAGES 4
typedef struct {
u8 pool[DRR_EMERGENCY_POOL_SIZE] __attribute__((aligned(64)));
u64 pool_offset;
u64 pool_limit;
/* 子缓冲区指针(pool 内偏移分配) */
u8 *stack_base;
u64 stack_size;
u64 stack_ptr;
drr_log_entry *log_base;
u32 log_count;
u32 log_capacity;
drr_checkpoint_meta *ckpt_meta_buf;
u8 *crash_buf;
u64 crash_buf_size;
void *meta_slab;
u64 meta_slab_offset;
/* A/B checkpoint slots */
drr_checkpoint_meta ckpt_a;
drr_checkpoint_meta ckpt_b;
u32 active_slot; /* 0=A, 1=B */
/* Root key */
const u64 *root_key;
/* Recovery generation */
u64 recovery_generation;
/* Heartbeat */
u64 heartbeat_counter;
u64 heartbeat_interval;
/* 初始化标记 */
int initialized;
} drr_state;
/* ---- 公共接口 ---- */
void drr_init(void);
const u64 *drr_get_root_key(void); const u64 *drr_get_root_key(void);
u64 drr_recovery_generation(void); u64 drr_recovery_generation(void);
void drr_report_fault(const char *reason); void drr_report_fault(const char *reason);
void drr_log_write_intent(u32 segment_slot, u64 offset, u64 len); void drr_log_write_intent(u32 segment_slot, u64 offset, u64 len);
void drr_log_write_commit(u32 segment_slot, u64 offset, u64 len); void drr_log_write_commit(u32 segment_slot, u64 offset, u64 len);
#endif /* Checkpoint 操作 */
int drr_checkpoint_begin(drr_checkpoint_type type);
int drr_checkpoint_finish(void);
int drr_checkpoint_recover(drr_checkpoint_meta *out_meta);
/* Emergency Pool 分配 */
void *drr_emergency_alloc(u64 size, u64 alignment);
void drr_emergency_reset(void);
/* Heartbeat */
void drr_heartbeat_tick(void);
u64 drr_heartbeat_get(void);
/* CRC64 */
u64 drr_crc64(const void *data, u64 len);
#endif /* UTSM_DRR_H */
+64 -3
View File
@@ -3,16 +3,77 @@
#include <utsm/types.h> #include <utsm/types.h>
#include <utsm/uuid.h> #include <utsm/uuid.h>
#include <utsm/capability.h>
#include <utsm/config.h>
/* ---- 进程标志 ---- */
#define UTSM_PROC_F_BOOTSTRAP (1U << 0) /* 内核 bootstrap 进程 */
#define UTSM_PROC_F_KERNEL (1U << 1) /* 内核态进程 */
#define UTSM_PROC_F_ENCRYPTED (1U << 2) /* 加密堆栈/堆 */
/* ---- 进程状态 ---- */
typedef enum {
UTSM_PROC_UNUSED = 0,
UTSM_PROC_ACTIVE,
UTSM_PROC_BLOCKED,
UTSM_PROC_TERMINATED
} utsm_process_state;
/* ---- Capability Table(每进程) ---- */
typedef struct {
utsm_capability caps[UTSM_MAX_CAPABILITIES];
u32 count;
u32 generation; /* 表版本号 */
} utsm_cap_table;
/* ---- Crypto Context(每进程) ---- */
typedef struct {
u64 crypto_epoch; /* 当前加密纪元 */
u64 key_derivation_seed; /* 密钥派生种子 */
u32 pckc_hot_hint; /* PCKC 热段提示 */
u32 reserved;
} utsm_crypto_context;
/* ---- 进程上下文 ---- */
typedef struct utsm_process_context { typedef struct utsm_process_context {
uuid128_t process_uuid; uuid128_t process_uuid;
u64 crypto_epoch; u64 crypto_epoch;
u64 recovery_generation; u64 recovery_generation;
u32 capability_table_slot; u32 process_slot; /* 进程表索引 */
u32 capability_count; u32 capability_count;
u32 flags; u32 flags;
utsm_process_state state;
/* Capability Table */
utsm_cap_table cap_table;
/* Crypto Context */
utsm_crypto_context crypto_ctx;
/* PCKC 热段提示 */
u32 hot_segment_slot;
u32 reserved;
/* DRR recovery generation */
u64 drr_recovery_gen;
} utsm_process_context; } utsm_process_context;
void utsm_process_create_test(utsm_process_context *process, u64 seed); /* ---- 进程表 ---- */
#define UTSM_MAX_PROCESSES 64
#endif /* ---- 公共接口 ---- */
void utsm_process_init(void);
void utsm_process_create_test(utsm_process_context *process, u64 seed);
utsm_process_context *utsm_process_alloc(void);
void utsm_process_free(utsm_process_context *process);
utsm_process_context *utsm_process_get(u32 slot);
/* Capability 操作 */
int utsm_process_grant_cap(utsm_process_context *process, utsm_capability cap);
int utsm_process_revoke_cap(utsm_process_context *process, u32 cap_index);
utsm_capability *utsm_process_get_cap(utsm_process_context *process, u32 cap_index);
/* Crypto context 操作 */
void utsm_process_advance_epoch(utsm_process_context *process);
#endif /* UTSM_PROCESS_H */
+3 -3
View File
@@ -28,9 +28,9 @@ void kernel_main(void) {
net_init(); net_init();
log_info("[UTSM] net registry init ok"); log_info("[UTSM] net registry init ok");
log_info("[UTSM] drr stub init begin"); log_info("[UTSM] drr init begin");
drr_stub_init(); drr_init();
log_info("[UTSM] drr stub init ok"); log_info("[UTSM] drr init ok");
log_info("[UTSM] core init begin"); log_info("[UTSM] core init begin");
utsm_init(); utsm_init();
+25
View File
@@ -0,0 +1,25 @@
CC := clang
LD := ld.lld
ROOT := ../..
OUT := $(ROOT)/SYSTEM/system/deshab64/desktop.elf
CFLAGS := -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
LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld
C_SOURCES := main.c
OBJECTS := $(C_SOURCES:.c=.o)
.PHONY: all clean
all: $(OUT)
$(OUT): $(OBJECTS) linker.ld
@if not exist "$(ROOT)\SYSTEM\system\deshab64" mkdir "$(ROOT)\SYSTEM\system\deshab64"
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }"
+16
View File
@@ -0,0 +1,16 @@
ENTRY(dsk_entry)
PHDRS
{
text PT_LOAD FLAGS(7);
rodata PT_LOAD FLAGS(4);
data PT_LOAD FLAGS(7);
}
SECTIONS
{
. = 0x1000;
.text : ALIGN(4K) { *(.text*) } :text
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
.data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data
}
+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");
}
}
+20 -20
View File
@@ -373,31 +373,31 @@ static void dsk_spinner_and_check(u32 *fb_arg, i64 cx, i64 cy, i64 r, i64 thk,
if(fe_entry&&frame>150){logl("[DSK] jumping to FirstInit"); /* was 600 */ if(fe_entry&&frame>150){logl("[DSK] jumping to FirstInit"); /* was 600 */
void(*entry)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))fe_entry; void(*entry)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))fe_entry;
entry(ctx); entry(ctx);
logl("[DSK] FirstInit returned, loading shell"); logl("[DSK] FirstInit returned, loading desktop");
/* FirstInit 返回后加载 shell */ /* FirstInit 返回后加载 desktop */
char sh[12]; /* SHELL ELF */ char dn[12]; /* DESKTOP ELF */
sh[0]='S';sh[1]='H';sh[2]='E';sh[3]='L';sh[4]='L';sh[5]=' ';sh[6]=' ';sh[7]=' ';sh[8]='E';sh[9]='L';sh[10]='F'; dn[0]='D';dn[1]='E';dn[2]='S';dn[3]='K';dn[4]='T';dn[5]='O';dn[6]='P';dn[7]=' ';dn[8]='E';dn[9]='L';dn[10]='F';
u8 *sd=0; u32 ss=0; void *se=0; u8 *dd=0; u32 ds=0; void *de=0;
if(fat32_read_root_file(sh,&sd,&ss)==0){ if(fat32_read_root_file(dn,&dd,&ds)==0){
if(dsk_load_elf(sd,ss,&se)==0){ if(dsk_load_elf(dd,ds,&de)==0){
logl("[DSK] jumping to shell"); logl("[DSK] jumping to desktop");
void(*sentry)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))se; void(*dentry)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))de;
sentry(ctx); dentry(ctx);
} }
} }
for(;;)__asm__("hlt"); for(;;)__asm__("hlt");
} }
/* 非首次启动:直接加载 shell */ /* 非首次启动:直接加载 desktop */
if(checked&&!fe_entry&&frame>150){ if(checked&&!fe_entry&&frame>150){
logl("[DSK] loading shell directly (non-first boot)"); logl("[DSK] loading desktop directly (non-first boot)");
char sh[12]; /* SHELL ELF */ char dn[12]; /* DESKTOP ELF */
sh[0]='S';sh[1]='H';sh[2]='E';sh[3]='L';sh[4]='L';sh[5]=' ';sh[6]=' ';sh[7]=' ';sh[8]='E';sh[9]='L';sh[10]='F'; dn[0]='D';dn[1]='E';dn[2]='S';dn[3]='K';dn[4]='T';dn[5]='O';dn[6]='P';dn[7]=' ';dn[8]='E';dn[9]='L';dn[10]='F';
u8 *sd=0; u32 ss=0; void *se=0; u8 *dd=0; u32 ds=0; void *de=0;
if(fat32_read_root_file(sh,&sd,&ss)==0){ if(fat32_read_root_file(dn,&dd,&ds)==0){
if(dsk_load_elf(sd,ss,&se)==0){ if(dsk_load_elf(dd,ds,&de)==0){
logl("[DSK] jumping to shell"); logl("[DSK] jumping to desktop");
void(*sentry)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))se; void(*dentry)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))de;
sentry(ctx); dentry(ctx);
} }
} }
for(;;)__asm__("hlt"); for(;;)__asm__("hlt");
+126 -163
View File
@@ -1,11 +1,10 @@
/* Deshab Shell — 命令行交互界面 /* Deshab Shell — 命令行交互界面
* 接收 PS/2 键盘输入,执行内置命令。 * 接收 PS/2 键盘输入,执行内置命令。
* 显示在 framebuffer 上,使用 Consolas 18px 等宽字体。 * 使用 Deshab "Sealed Arc" 视觉风格系统。
*/ */
#include "../UTSM/include/utsm/dsk.h" #include "../UTSM/include/utsm/dsk.h"
typedef signed char i8;
typedef unsigned char u8; typedef unsigned char u8;
typedef unsigned short u16; typedef unsigned short u16;
typedef unsigned int u32; typedef unsigned int u32;
@@ -24,28 +23,37 @@ static void sputc(char c) {
static void swrite(const char *s) { while(*s) { if(*s=='\n')sputc('\r'); sputc(*s++); } } static void swrite(const char *s) { while(*s) { if(*s=='\n')sputc('\r'); sputc(*s++); } }
static void logl(const char *s) { swrite(s); swrite("\n"); } static void logl(const char *s) { swrite(s); swrite("\n"); }
/* ASCII 字体 — Consolas 18px 灰度位图(与 FirstInit 共享) */ /* ASCII 字体 — 必须在 deshab_ui.h 之前包含,因为 du_draw_char 引用 g_ascii */
#include "../firstInit/ascii_bitmaps.c" #include "../firstInit/ascii_bitmaps.c"
#include "../UTSM/include/utsm/deshab_ui.h"
#define ASCII_W 11 /* ---- 风格令牌(引用 deshab_ui.h 语义色) ---- */
#define ASCII_H 18 #define BG_COLOR DS_DARK_BG_PRIMARY
#define ASCII_STEP 11 #define TEXT_FG DS_DARK_TEXT_PRIMARY
#define PROMPT_FG DS_DARK_PROMPT
#define ERROR_FG DP_ERROR
#define OK_FG DP_SUCCESS
#define DIM_FG DS_DARK_TEXT_DIM
#define ACCENT_FG DS_DARK_ACCENT
#define DECORATIVE_FG DS_DARK_DECORATIVE
/* 标题栏参数 */
#define TITLEBAR_H 28
#define TITLEBAR_BG DP_ABYSS_800
#define TITLEBAR_FG DS_DARK_TEXT_SECONDARY
#define TITLEBAR_ACCENT DS_DARK_ACCENT
/* 状态栏参数 */
#define STATUSBAR_H 22
#define STATUSBAR_BG DP_ABYSS_800
/* 终端尺寸 */ /* 终端尺寸 */
#define TERM_COLS 100 #define TERM_COLS 100
#define TERM_ROWS 35 #define TERM_ROWS 35
#define TERM_MAX_CHARS (TERM_COLS * TERM_ROWS) #define TERM_MAX_CHARS (TERM_COLS * TERM_ROWS)
/* 颜色 — 深色主题,类似经典终端 */ static du_context g_ctx;
#define BG_COLOR 0xFF0A1428u /* 深蓝黑 */
#define TEXT_FG 0xFFE0E8F0u /* 浅灰白 */
#define PROMPT_FG 0xFF66CCFFu /* 青色提示符 */
#define ERROR_FG 0xFFFF6680u /* 红色错误 */
#define OK_FG 0xFF66FF99u /* 绿色成功 */
#define DIM_FG 0xFF7080A0u /* 灰色注释 */
static u64 fb_a, fb_w, fb_h, fb_p; static u64 fb_a, fb_w, fb_h, fb_p;
static u32 *g_fb;
/* 终端缓冲区 — 字符 + 颜色 */ /* 终端缓冲区 — 字符 + 颜色 */
static u8 term_ch[TERM_MAX_CHARS]; static u8 term_ch[TERM_MAX_CHARS];
@@ -56,61 +64,18 @@ static int cur_col = 0;
static int cur_row = 0; static int cur_row = 0;
static int cur_visible = 1; static int cur_visible = 1;
/* 颜色混合 */ /* ---- 终端像素坐标 ---- */
static u32 blend(u32 c1, u32 c2, u32 a) { static int term_start_x, term_start_y;
u32 na=256-a;
u32 r=((c1&0xFF)*na+(c2&0xFF)*a)>>8;
u32 g=(((c1>>8)&0xFF)*na+((c2>>8)&0xFF)*a)>>8;
u32 b=(((c1>>16)&0xFF)*na+((c2>>16)&0xFF)*a)>>8;
return 0xFF000000|(b<<16)|(g<<8)|r;
}
/* ---- 字符渲染(与 FirstInit 类似,但用纯色背景) ---- */
static void fb_char(u32 *fb, u32 ch, i64 x, i64 y, u32 fg, u32 bg) {
if (ch < ' ' || ch > '~') ch = ' ';
u32 idx = (u32)(ch - ' ');
const u8 *g = g_ascii[idx];
for (i64 r=0; r<ASCII_H; r++) {
i64 yy = y + r;
if (yy < 0 || (u64)yy >= fb_h) continue;
u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p);
for (i64 c=0; c<ASCII_W; c++) {
u32 a = g[r * ASCII_W + c];
if (a == 0) continue;
i64 xx = x + c;
if (xx < 0 || (u64)xx >= fb_w) continue;
line[(u64)xx] = (a == 255) ? fg : blend(bg, fg, a);
}
}
}
static void fill_rect(u32 *fb, i64 x, i64 y, i64 w, i64 h, u32 color) {
for (i64 r=0; r<h; r++) {
i64 yy = y + r;
if (yy < 0 || (u64)yy >= fb_h) continue;
u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p);
for (i64 c=0; c<w; c++) {
i64 xx = x + c;
if (xx < 0 || (u64)xx >= fb_w) continue;
line[(u64)xx] = color;
}
}
}
/* ---- 终端逻辑 ---- */
static int term_start_x, term_start_y; /* 像素坐标 */
static void term_init_pos(void) { static void term_init_pos(void) {
/* 居中布局,每字符 11px 宽,行高 20px */ int margin_x = (int)DU_SPACE_MD;
int margin_x = 16;
int margin_y = 16;
term_start_x = margin_x; term_start_x = margin_x;
term_start_y = margin_y; /* 标题栏下方开始 */
/* 自适应行数/列数到 framebuffer */ term_start_y = TITLEBAR_H + (int)DU_SPACE_SM;
int avail_w = (int)fb_w - margin_x * 2; int avail_w = (int)fb_w - margin_x * 2;
int avail_h = (int)fb_h - margin_y * 2; int avail_h = (int)fb_h - term_start_y - STATUSBAR_H - (int)DU_SPACE_SM;
term_w = avail_w / ASCII_STEP; term_w = avail_w / (int)DU_ASCII_STEP;
term_h = avail_h / (ASCII_H + 2); term_h = avail_h / (int)DU_ASCII_LINE_H;
if (term_w < 10) term_w = 10; if (term_w < 10) term_w = 10;
if (term_h < 5) term_h = 5; if (term_h < 5) term_h = 5;
if (term_w > TERM_COLS) term_w = TERM_COLS; if (term_w > TERM_COLS) term_w = TERM_COLS;
@@ -127,7 +92,6 @@ static void term_clear(void) {
} }
static void term_scroll(void) { static void term_scroll(void) {
/* 向上滚动一行 */
for (int r = 1; r < term_h; r++) { for (int r = 1; r < term_h; r++) {
for (int c = 0; c < term_w; c++) { for (int c = 0; c < term_w; c++) {
int src = r * TERM_COLS + c; int src = r * TERM_COLS + c;
@@ -136,7 +100,6 @@ static void term_scroll(void) {
term_fg[dst] = term_fg[src]; term_fg[dst] = term_fg[src];
} }
} }
/* 清空最后一行 */
for (int c = 0; c < term_w; c++) { for (int c = 0; c < term_w; c++) {
term_ch[(term_h-1) * TERM_COLS + c] = ' '; term_ch[(term_h-1) * TERM_COLS + c] = ' ';
term_fg[(term_h-1) * TERM_COLS + c] = TEXT_FG; term_fg[(term_h-1) * TERM_COLS + c] = TEXT_FG;
@@ -168,57 +131,96 @@ static void term_putc_color(char c, u32 color) {
} }
static void term_putc(char c) { term_putc_color(c, TEXT_FG); } static void term_putc(char c) { term_putc_color(c, TEXT_FG); }
static void term_puts_color(const char *s, u32 color) { while (*s) term_putc_color(*s++, color); }
static void term_puts(const char *s) { term_puts_color(s, TEXT_FG); }
static void term_puts_color(const char *s, u32 color) { /* ---- 标题栏渲染 ---- */
while (*s) term_putc_color(*s++, color); static void draw_titlebar(void) {
du_fill_rect(&g_ctx, 0, 0, (i64)fb_w, TITLEBAR_H, TITLEBAR_BG);
/* 底部强调线 */
du_fill_rect(&g_ctx, 0, TITLEBAR_H - 2, (i64)fb_w, 2, TITLEBAR_ACCENT);
/* 左侧标题文字 */
du_draw_string(&g_ctx, "Deshab Shell",
(i64)DU_SPACE_MD, (TITLEBAR_H - (i64)DU_ASCII_CELL_H) / 2,
DS_DARK_TEXT_PRIMARY, TITLEBAR_BG, DU_ASCII_STEP);
/* 右侧版本号 */
du_draw_string(&g_ctx, "v0.1.0",
(i64)fb_w - 6 * (i64)DU_ASCII_STEP - (i64)DU_SPACE_MD,
(TITLEBAR_H - (i64)DU_ASCII_CELL_H) / 2,
DS_DARK_TEXT_DIM, TITLEBAR_BG, DU_ASCII_STEP);
} }
static void term_puts(const char *s) { term_puts_color(s, TEXT_FG); } /* ---- 状态栏渲染 ---- */
static void draw_statusbar(void) {
i64 sy = (i64)fb_h - STATUSBAR_H;
du_fill_rect(&g_ctx, 0, sy, (i64)fb_w, STATUSBAR_H, STATUSBAR_BG);
/* 顶部强调线 */
du_fill_rect(&g_ctx, 0, sy, (i64)fb_w, 1, DS_DARK_DIVIDER);
/* 左侧:当前路径 */
du_draw_string(&g_ctx, "/",
(i64)DU_SPACE_MD, sy + (STATUSBAR_H - (i64)DU_ASCII_CELL_H) / 2,
DS_DARK_TEXT_DIM, STATUSBAR_BG, DU_ASCII_STEP);
/* 右侧:root@deshab */
const char *user = "root@deshab";
int ulen = 0;
while (user[ulen]) ulen++;
du_draw_string(&g_ctx, user,
(i64)fb_w - ulen * (i64)DU_ASCII_STEP - (i64)DU_SPACE_MD,
sy + (STATUSBAR_H - (i64)DU_ASCII_CELL_H) / 2,
DS_DARK_ACCENT, STATUSBAR_BG, DU_ASCII_STEP);
}
static void term_redraw_all(void) { static void term_redraw_all(void) {
/* 清屏背景 */ /* 清屏背景 */
fill_rect(g_fb, 0, 0, (i64)fb_w, (i64)fb_h, BG_COLOR); du_fill_bg_solid(&g_ctx, BG_COLOR);
/* 标题栏 */
draw_titlebar();
/* 渲染所有字符 */ /* 渲染所有字符 */
for (int r = 0; r < term_h; r++) { for (int r = 0; r < term_h; r++) {
for (int c = 0; c < term_w; c++) { for (int c = 0; c < term_w; c++) {
int idx = r * TERM_COLS + c; int idx = r * TERM_COLS + c;
u8 ch = term_ch[idx]; u8 ch = term_ch[idx];
if (ch == ' ') continue; if (ch == ' ') continue;
i64 x = term_start_x + (i64)c * ASCII_STEP; i64 x = term_start_x + (i64)c * (i64)DU_ASCII_STEP;
i64 y = term_start_y + (i64)r * (ASCII_H + 2); i64 y = term_start_y + (i64)r * (i64)DU_ASCII_LINE_H;
fb_char(g_fb, ch, x, y, term_fg[idx], BG_COLOR); du_draw_char(&g_ctx, ch, x, y, term_fg[idx], BG_COLOR);
} }
} }
/* 渲染光标 */ /* 光标 */
if (cur_visible && cur_col < term_w && cur_row < term_h) { if (cur_visible && cur_col < term_w && cur_row < term_h) {
i64 x = term_start_x + (i64)cur_col * ASCII_STEP; i64 x = term_start_x + (i64)cur_col * (i64)DU_ASCII_STEP;
i64 y = term_start_y + (i64)cur_row * (ASCII_H + 2); i64 y = term_start_y + (i64)cur_row * (i64)DU_ASCII_LINE_H;
fill_rect(g_fb, x, y + ASCII_H - 3, ASCII_W, 2, TEXT_FG); du_fill_rect(&g_ctx, x, y + (i64)DU_ASCII_CELL_H - 3,
(i64)DU_ASCII_CELL_W, 2, DS_DARK_CURSOR);
} }
/* 状态栏 */
draw_statusbar();
} }
static void term_redraw_cursor(void) { static void term_redraw_cursor(void) {
if (cur_visible && cur_col < term_w && cur_row < term_h) { if (cur_visible && cur_col < term_w && cur_row < term_h) {
i64 x = term_start_x + (i64)cur_col * ASCII_STEP; i64 x = term_start_x + (i64)cur_col * (i64)DU_ASCII_STEP;
i64 y = term_start_y + (i64)cur_row * (ASCII_H + 2); i64 y = term_start_y + (i64)cur_row * (i64)DU_ASCII_LINE_H;
fill_rect(g_fb, x, y + ASCII_H - 3, ASCII_W, 2, TEXT_FG); du_fill_rect(&g_ctx, x, y + (i64)DU_ASCII_CELL_H - 3,
(i64)DU_ASCII_CELL_W, 2, DS_DARK_CURSOR);
} }
} }
static void term_clear_cursor(void) { static void term_clear_cursor(void) {
if (cur_col < term_w && cur_row < term_h) { if (cur_col < term_w && cur_row < term_h) {
i64 x = term_start_x + (i64)cur_col * ASCII_STEP; i64 x = term_start_x + (i64)cur_col * (i64)DU_ASCII_STEP;
i64 y = term_start_y + (i64)cur_row * (ASCII_H + 2); i64 y = term_start_y + (i64)cur_row * (i64)DU_ASCII_LINE_H;
fill_rect(g_fb, x, y + ASCII_H - 3, ASCII_W, 2, BG_COLOR); du_fill_rect(&g_ctx, x, y + (i64)DU_ASCII_CELL_H - 3,
(i64)DU_ASCII_CELL_W, 2, BG_COLOR);
} }
} }
/* ---- 输入缓冲 ---- */ /* ---- 输入缓冲 ---- */
static char input_buf[256]; static char input_buf[256];
static int input_len = 0; static int input_len = 0;
static int input_cursor = 0; /* 在 input_buf 中的位置(支持左右移动) */ static int input_cursor = 0;
/* ---- 键盘扫描码 → ASCII(Set 1,与 FirstInit 共享逻辑) ---- */ /* ---- 键盘扫描码 → ASCII(Set 1) ---- */
static char scan_to_ascii(u8 sc, int shift) { static char scan_to_ascii(u8 sc, int shift) {
static const char normal[58] = { static const char normal[58] = {
0, 27, '1','2','3','4','5','6','7','8','9','0','-','=', 8, '\t', 0, 27, '1','2','3','4','5','6','7','8','9','0','-','=', 8, '\t',
@@ -264,6 +266,7 @@ static void cmd_version(void) {
term_puts(" Architecture: x86_64\n"); term_puts(" Architecture: x86_64\n");
term_puts(" ABI: DKM (Deshab Kernel Module) v1\n"); term_puts(" ABI: DKM (Deshab Kernel Module) v1\n");
term_puts(" Bootloader: Limine\n"); term_puts(" Bootloader: Limine\n");
term_puts_color(" Style: Sealed Arc (Deshab UI)\n", DIM_FG);
} }
static void cmd_uname(void) { static void cmd_uname(void) {
@@ -274,20 +277,20 @@ static void cmd_about(void) {
term_puts_color("=== Deshab OS ===\n", PROMPT_FG); term_puts_color("=== Deshab OS ===\n", PROMPT_FG);
term_puts("单地址空间 Ring0 内核实验系统\n"); term_puts("单地址空间 Ring0 内核实验系统\n");
term_puts("架构: UTSM + DSK + DKM 驱动模型\n"); term_puts("架构: UTSM + DSK + DKM 驱动模型\n");
term_puts_color("视觉: Sealed Arc 设计语言\n", DECORATIVE_FG);
term_puts_color("Built with Clang + lld\n", DIM_FG); term_puts_color("Built with Clang + lld\n", DIM_FG);
term_puts_color("© 2026 Deshab Project\n", DIM_FG); term_puts_color("(c) 2026 Deshab Project\n", DIM_FG);
} }
static void cmd_clear(void) { static void cmd_clear(void) {
term_clear(); term_clear();
term_redraw_all();
} }
static void cmd_reboot(void) { static void cmd_reboot(void) {
term_puts_color("Rebooting...\n", PROMPT_FG); term_puts_color("Rebooting...\n", PROMPT_FG);
term_redraw_all(); term_redraw_all();
/* ACPI 重启:写 0x64 = 0xFE 触发键盘控制器复位 */
outb(0x64, 0xFE); outb(0x64, 0xFE);
/* 备用:三重故障 */
__asm__ volatile("int $0x03"); __asm__ volatile("int $0x03");
for(;;) __asm__("hlt"); for(;;) __asm__("hlt");
} }
@@ -303,22 +306,18 @@ static void cmd_echo(const char *args) {
term_putc('\n'); term_putc('\n');
} }
/* 前向声明 — CMOS RTC 读取 */
static u8 read_rtc_reg(u8 reg); static u8 read_rtc_reg(u8 reg);
static u8 bcd_to_bin_impl(u8 bcd); static u8 bcd_to_bin_impl(u8 bcd);
static void cmd_date(void) { static void cmd_date(void) {
/* 通过 CMOS RTC 读取日期 */
u8 year = bcd_to_bin_impl(read_rtc_reg(9)); u8 year = bcd_to_bin_impl(read_rtc_reg(9));
u8 month = bcd_to_bin_impl(read_rtc_reg(8)); u8 month = bcd_to_bin_impl(read_rtc_reg(8));
u8 day = bcd_to_bin_impl(read_rtc_reg(7)); u8 day = bcd_to_bin_impl(read_rtc_reg(7));
u8 hour = bcd_to_bin_impl(read_rtc_reg(4)); u8 hour = bcd_to_bin_impl(read_rtc_reg(4));
u8 minute = bcd_to_bin_impl(read_rtc_reg(2)); u8 minute = bcd_to_bin_impl(read_rtc_reg(2));
u8 second = bcd_to_bin_impl(read_rtc_reg(0)); u8 second = bcd_to_bin_impl(read_rtc_reg(0));
/* 简单打印 */
char buf[32]; char buf[32];
int p = 0; int p = 0;
/* YYYY-MM-DD HH:MM:SS */
buf[p++] = '2'; buf[p++] = '0'; buf[p++] = '2'; buf[p++] = '0';
buf[p++] = '0' + year / 10; buf[p++] = '0' + year % 10; buf[p++] = '0' + year / 10; buf[p++] = '0' + year % 10;
buf[p++] = '-'; buf[p++] = '-';
@@ -349,21 +348,16 @@ static void cmd_not_impl(const char *cmd) {
} }
static void execute_command(const char *cmd) { static void execute_command(const char *cmd) {
/* 跳过前导空格 */
while (*cmd == ' ') cmd++; while (*cmd == ' ') cmd++;
if (*cmd == 0) return; if (*cmd == 0) return;
/* 找命令名(第一个单词) */
const char *args = cmd; const char *args = cmd;
while (*args && *args != ' ') args++; while (*args && *args != ' ') args++;
int cmd_name_len = (int)(args - cmd); int cmd_name_len = (int)(args - cmd);
/* 复制命令名到本地缓冲 */
char name[32]; char name[32];
if (cmd_name_len >= 32) cmd_name_len = 31; if (cmd_name_len >= 32) cmd_name_len = 31;
for (int i = 0; i < cmd_name_len; i++) name[i] = cmd[i]; for (int i = 0; i < cmd_name_len; i++) name[i] = cmd[i];
name[cmd_name_len] = 0; name[cmd_name_len] = 0;
/* 跳过参数前的空格 */
while (*args == ' ') args++; while (*args == ' ') args++;
/* 分派 */
if (str_eq(name, "help") || str_eq(name, "?")) cmd_help(); if (str_eq(name, "help") || str_eq(name, "?")) cmd_help();
else if (str_eq(name, "version") || str_eq(name, "ver")) cmd_version(); else if (str_eq(name, "version") || str_eq(name, "ver")) cmd_version();
else if (str_eq(name, "uname")) cmd_uname(); else if (str_eq(name, "uname")) cmd_uname();
@@ -402,30 +396,26 @@ static void draw_prompt(void) {
/* ---- 输入行编辑 ---- */ /* ---- 输入行编辑 ---- */
static void redraw_input_line(void) { static void redraw_input_line(void) {
/* 清除当前行从提示符开始的部分并重绘 */
int prompt_len = 0; int prompt_len = 0;
while (PROMPT[prompt_len]) prompt_len++; while (PROMPT[prompt_len]) prompt_len++;
/* 清除整行 */ i64 y = term_start_y + (i64)cur_row * (i64)DU_ASCII_LINE_H;
i64 y = term_start_y + (i64)cur_row * (ASCII_H + 2); du_fill_rect(&g_ctx, term_start_x, y,
fill_rect(g_fb, term_start_x, y, (i64)term_w * ASCII_STEP, ASCII_H, BG_COLOR); (i64)term_w * (i64)DU_ASCII_STEP,
/* 重绘提示符 */ (i64)DU_ASCII_CELL_H, BG_COLOR);
for (int i = 0; i < prompt_len; i++) { for (int i = 0; i < prompt_len; i++) {
i64 x = term_start_x + (i64)i * ASCII_STEP; i64 x = term_start_x + (i64)i * (i64)DU_ASCII_STEP;
fb_char(g_fb, PROMPT[i], x, y, PROMPT_FG, BG_COLOR); du_draw_char(&g_ctx, PROMPT[i], x, y, PROMPT_FG, BG_COLOR);
} }
/* 重绘输入缓冲 */
for (int i = 0; i < input_len; i++) { for (int i = 0; i < input_len; i++) {
i64 x = term_start_x + (i64)(prompt_len + i) * ASCII_STEP; i64 x = term_start_x + (i64)(prompt_len + i) * (i64)DU_ASCII_STEP;
fb_char(g_fb, input_buf[i], x, y, TEXT_FG, BG_COLOR); du_draw_char(&g_ctx, input_buf[i], x, y, TEXT_FG, BG_COLOR);
} }
/* 同步 cur_col 到输入光标 */
cur_col = prompt_len + input_cursor; cur_col = prompt_len + input_cursor;
term_redraw_cursor(); term_redraw_cursor();
} }
static void insert_char(char c) { static void insert_char(char c) {
if (input_len >= 255) return; if (input_len >= 255) return;
/* 在 input_cursor 处插入 */
for (int i = input_len; i > input_cursor; i--) { for (int i = input_len; i > input_cursor; i--) {
input_buf[i] = input_buf[i-1]; input_buf[i] = input_buf[i-1];
} }
@@ -437,7 +427,6 @@ static void insert_char(char c) {
static void delete_char_back(void) { static void delete_char_back(void) {
if (input_cursor == 0) return; if (input_cursor == 0) return;
/* 删除 input_cursor-1 处的字符 */
for (int i = input_cursor - 1; i < input_len - 1; i++) { for (int i = input_cursor - 1; i < input_len - 1; i++) {
input_buf[i] = input_buf[i+1]; input_buf[i] = input_buf[i+1];
} }
@@ -523,14 +512,16 @@ void dsk_entry(const dsk_boot_context *ctx) {
fb_w = ctx->framebuffer_width; fb_w = ctx->framebuffer_width;
fb_h = ctx->framebuffer_height; fb_h = ctx->framebuffer_height;
fb_p = ctx->framebuffer_pitch; fb_p = ctx->framebuffer_pitch;
g_fb = (u32 *)(u64)fb_a;
/* 初始化风格系统渲染上下文 */
du_context_init(&g_ctx, fb_a, fb_w, fb_h, fb_p);
term_init_pos(); term_init_pos();
term_clear(); term_clear();
term_redraw_all(); term_redraw_all();
/* 启动 banner */ /* 启动 banner — 使用风格令牌 */
term_puts_color("=== Deshab OS v0.1.0 ===\n", PROMPT_FG); term_puts_color("=== Deshab OS v0.1.0 ===\n", ACCENT_FG);
term_puts_color("欢迎使用 Deshab Shell\n", OK_FG); term_puts_color("欢迎使用 Deshab Shell\n", OK_FG);
term_puts_color("输入 'help' 查看可用命令\n", DIM_FG); term_puts_color("输入 'help' 查看可用命令\n", DIM_FG);
term_putc('\n'); term_putc('\n');
@@ -548,47 +539,40 @@ void dsk_entry(const dsk_boot_context *ctx) {
cur_col = prompt_len; cur_col = prompt_len;
term_redraw_cursor(); term_redraw_cursor();
/* 等待命令输入 */
int cmd_done = 0; int cmd_done = 0;
while (!cmd_done) { while (!cmd_done) {
u8 st = inb(0x64); u8 st = inb(0x64);
if (!(st & 1)) { __asm__("pause"); continue; } if (!(st & 1)) { __asm__("pause"); continue; }
u8 data = inb(0x60); u8 data = inb(0x60);
/* 忽略鼠标数据(AUX 位) */
if (st & 0x20) continue; if (st & 0x20) continue;
u8 sc = data; u8 sc = data;
if (sc == 0xE0) { e0 = 1; continue; } if (sc == 0xE0) { e0 = 1; continue; }
if (sc == 0x2A || sc == 0x36) { shift = 1; continue; } if (sc == 0x2A || sc == 0x36) { shift = 1; continue; }
if (sc == 0xAA || sc == 0xB6) { shift = 0; continue; } if (sc == 0xAA || sc == 0xB6) { shift = 0; continue; }
if (sc & 0x80) { if (sc & 0x80) {
/* 松开事件 — 仅处理扩展键 */ if (e0 && sc == 0x9C) { e0 = 0; }
if (e0 && sc == 0x9C) { e0 = 0; } /* Enter 释放 */ else if (e0 && sc == 0xCB) { e0 = 0; }
else if (e0 && sc == 0xCB) { e0 = 0; } /* Left 释放 */ else if (e0 && sc == 0xCD) { e0 = 0; }
else if (e0 && sc == 0xCD) { e0 = 0; } /* Right 释放 */ else if (e0 && sc == 0xC8) { e0 = 0; }
else if (e0 && sc == 0xC8) { e0 = 0; } /* Up 释放 */ else if (e0 && sc == 0xD0) { e0 = 0; }
else if (e0 && sc == 0xD0) { e0 = 0; } /* Down 释放 */ else if (e0 && sc == 0xD3) { e0 = 0; }
else if (e0 && sc == 0xD3) { e0 = 0; } /* Delete 释放 */ else if (e0 && sc == 0xC7) { e0 = 0; }
else if (e0 && sc == 0xC7) { e0 = 0; } /* Home 释放 */ else if (e0 && sc == 0xCF) { e0 = 0; }
else if (e0 && sc == 0xCF) { e0 = 0; } /* End 释放 */
e0 = 0; e0 = 0;
continue; continue;
} }
/* 按下事件 */
if (e0) { if (e0) {
/* 扩展键 */ if (sc == 0x4B) cursor_left();
if (sc == 0x4B) cursor_left(); /* Left */ else if (sc == 0x4D) cursor_right();
else if (sc == 0x4D) cursor_right(); /* Right */ else if (sc == 0x47) cursor_home();
else if (sc == 0x47) cursor_home(); /* Home */ else if (sc == 0x4F) cursor_end();
else if (sc == 0x4F) cursor_end(); /* End */ else if (sc == 0x53) delete_char_fwd();
else if (sc == 0x53) delete_char_fwd(); /* Delete */ else if (sc == 0x48) { }
else if (sc == 0x48) { /* Up — 命令历史暂未实现 */ } else if (sc == 0x50) { }
else if (sc == 0x50) { /* Down — 命令历史暂未实现 */ }
e0 = 0; e0 = 0;
continue; continue;
} }
/* 控制键 */
if (sc == 0x1C) { if (sc == 0x1C) {
/* Enter — 执行命令 */
term_clear_cursor(); term_clear_cursor();
input_buf[input_len] = 0; input_buf[input_len] = 0;
term_putc('\n'); term_putc('\n');
@@ -596,32 +580,11 @@ void dsk_entry(const dsk_boot_context *ctx) {
cmd_done = 1; cmd_done = 1;
continue; continue;
} }
if (sc == 0x0E) { if (sc == 0x0E) { delete_char_back(); continue; }
/* Backspace */ if (sc == 0x01) { kill_line(); continue; }
delete_char_back(); if (sc == 0x15 && !shift) { kill_line(); continue; }
continue; if (sc == 0x17 && !shift) { while (input_cursor > 0) delete_char_back(); continue; }
} if (sc == 0x0B && !shift) { kill_to_end(); continue; }
if (sc == 0x01) {
/* Esc — 清行 */
kill_line();
continue;
}
/* Ctrl+U (kill line) */
if (sc == 0x15 && !shift) {
kill_line();
continue;
}
if (sc == 0x17 && !shift) {
/* Ctrl+W — 删除到行首 */
while (input_cursor > 0) delete_char_back();
continue;
}
if (sc == 0x0B && !shift) {
/* Ctrl+K — 删除到行尾 */
kill_to_end();
continue;
}
/* 普通字符 */
char c = scan_to_ascii(sc, shift); char c = scan_to_ascii(sc, shift);
if (c && c >= 32 && c <= 126) { if (c && c >= 32 && c <= 126) {
insert_char(c); insert_char(c);
+25
View File
@@ -0,0 +1,25 @@
CC := clang
LD := ld.lld
ROOT := ../../..
OUT := $(ROOT)/SYSTEM/system/deshab64/tools/bash.elf
CFLAGS := -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.. -I../../UTSM/include -O2
LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld
C_SOURCES := main.c
OBJECTS := $(C_SOURCES:.c=.o)
.PHONY: all clean
all: $(OUT)
$(OUT): $(OBJECTS) linker.ld
@if not exist "$(ROOT)\SYSTEM\system\deshab64\tools" mkdir "$(ROOT)\SYSTEM\system\deshab64\tools"
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }"
+16
View File
@@ -0,0 +1,16 @@
ENTRY(dsk_entry)
PHDRS
{
text PT_LOAD FLAGS(7);
rodata PT_LOAD FLAGS(4);
data PT_LOAD FLAGS(7);
}
SECTIONS
{
. = 0x1000;
.text : ALIGN(4K) { *(.text*) } :text
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
.data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data
}
+456
View File
@@ -0,0 +1,456 @@
/* Deshab Bash — 全屏终端应用
* PS/2 键盘输入,命令行编辑,内置命令。
* Esc 键退出返回 DSK。
*/
#include "../../firstInit/ascii_bitmaps.c"
#include "../desktop_app.h"
/* ---- 终端参数 ---- */
#define TERM_COLS 120
#define TERM_ROWS 40
#define TERM_MAX_CHARS (TERM_COLS * TERM_ROWS)
#define CHAR_STEP 12
#define CHAR_H 18
#define PROMPT_STR "deshab# "
static da_app_context g_ac;
/* 终端缓冲区 */
static u8 term_ch[TERM_MAX_CHARS];
static u32 term_fg[TERM_MAX_CHARS];
static int term_w = TERM_COLS;
static int term_h = TERM_ROWS;
static int cur_col = 0;
static int cur_row = 0;
/* 输入缓冲 */
static char input_buf[256];
static int input_len = 0;
static int input_cursor = 0;
/* 命令历史 */
#define HIST_SIZE 16
static char history[HIST_SIZE][256];
static int hist_count = 0;
static int hist_idx = 0;
/* 坐标 */
static int term_x, term_y;
static int content_h;
static void term_init_layout(void) {
term_x = 8;
term_y = DA_TITLEBAR_H + 4;
int avail_w = (int)g_ac.fb_w - 16;
int avail_h = (int)g_ac.fb_h - term_y - DA_STATUSBAR_H - 8;
term_w = avail_w / CHAR_STEP;
term_h = avail_h / CHAR_H;
if (term_w < 10) term_w = 10;
if (term_h < 5) term_h = 5;
if (term_w > TERM_COLS) term_w = TERM_COLS;
if (term_h > TERM_ROWS) term_h = TERM_ROWS;
content_h = term_h * CHAR_H;
}
static void term_clear(void) {
for (int i = 0; i < TERM_MAX_CHARS; i++) {
term_ch[i] = ' ';
term_fg[i] = DA_TEXT_PRIMARY;
}
cur_col = 0; cur_row = 0;
}
static void term_scroll(void) {
for (int r = 1; r < term_h; r++) {
for (int c = 0; c < term_w; c++) {
term_ch[(r-1)*TERM_COLS+c] = term_ch[r*TERM_COLS+c];
term_fg[(r-1)*TERM_COLS+c] = term_fg[r*TERM_COLS+c];
}
}
for (int c = 0; c < term_w; c++) {
term_ch[(term_h-1)*TERM_COLS+c] = ' ';
term_fg[(term_h-1)*TERM_COLS+c] = DA_TEXT_PRIMARY;
}
cur_row = term_h - 1; cur_col = 0;
}
static void term_newline(void) {
cur_col = 0; cur_row++;
if (cur_row >= term_h) term_scroll();
}
static void term_putc_color(char c, u32 color) {
if (c == '\n') { term_newline(); return; }
if (c == '\r') { cur_col = 0; return; }
if (c == '\t') { int sp = 4 - (cur_col % 4); for (int i = 0; i < sp; i++) term_putc_color(' ', color); return; }
if (c < ' ' || c > '~') c = '?';
if (cur_col >= term_w) term_newline();
int idx = cur_row * TERM_COLS + cur_col;
term_ch[idx] = (u8)c; term_fg[idx] = color;
cur_col++;
}
static void term_putc(char c) { term_putc_color(c, DA_TEXT_PRIMARY); }
static void term_puts_color(const char *s, u32 color) { while (*s) term_putc_color(*s++, color); }
static void term_puts(const char *s) { term_puts_color(s, DA_TEXT_PRIMARY); }
/* ---- 绘制 ---- */
static void redraw_all(void) {
da_fill_bg(&g_ac, DA_BG_PRIMARY);
da_draw_titlebar(&g_ac, "Deshab Bash", (i64)g_ac.fb_w);
/* 终端字符 */
for (int r = 0; r < term_h; r++) {
for (int c = 0; c < term_w; c++) {
int idx = r * TERM_COLS + c;
if (term_ch[idx] == ' ') continue;
da_draw_char(&g_ac, term_ch[idx],
term_x + c * CHAR_STEP, term_y + r * CHAR_H,
term_fg[idx], DA_BG_PRIMARY);
}
}
/* 光标 */
if (cur_col < term_w && cur_row < term_h) {
da_fill_rect(&g_ac, term_x + cur_col * CHAR_STEP,
term_y + cur_row * CHAR_H + CHAR_H - 3, 11, 2, DA_ACCENT);
}
/* 状态栏 */
char status[64];
int p = 0;
const char *s1 = "/ root@deshab ";
while (*s1) status[p++] = *s1++;
u8 hh, mm; da_rtc_time(&hh, &mm);
status[p++] = '0' + hh/10; status[p++] = '0' + hh%10;
status[p++] = ':'; status[p++] = '0' + mm/10; status[p++] = '0' + mm%10;
status[p] = 0;
da_draw_statusbar(&g_ac, status, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
}
/* ---- 内置命令 ---- */
static int str_eq(const char *a, const char *b) {
while (*a && *b) { if (*a != *b) return 0; a++; b++; }
return *a == *b;
}
static void cmd_help(void) {
term_puts_color("Deshab Bash - 内置命令:\n", DA_PROMPT_COLOR);
term_puts(" help 显示此帮助\n");
term_puts(" clear 清屏\n");
term_puts(" echo <text> 显示文本\n");
term_puts(" version 显示系统版本\n");
term_puts(" ver 同 version\n");
term_puts(" uname 显示系统名称\n");
term_puts(" date 显示当前日期时间\n");
term_puts(" about 关于 Deshab\n");
term_puts(" reboot 重启系统\n");
term_puts(" halt 关机\n");
term_puts(" pwd 显示当前目录\n");
term_puts(" whoami 显示当前用户\n");
term_puts(" id 显示用户ID\n");
term_puts(" ls 列出根目录文件\n");
term_puts(" cat <file> 显示文件内容\n");
}
static void cmd_version(void) {
term_puts_color("Deshab OS v0.1.0\n", DA_SUCCESS);
term_puts(" Kernel: DSK (Deshab System Kernel)\n");
term_puts(" Architecture: x86_64\n");
term_puts(" ABI: DKM v1\n");
term_puts(" Bootloader: Limine\n");
}
static void cmd_uname(void) { term_puts("Deshab\n"); }
static void cmd_about(void) {
term_puts_color("=== Deshab OS ===\n", DA_PROMPT_COLOR);
term_puts("单地址空间 Ring0 内核实验系统\n");
term_puts("架构: UTSM + DSK + DKM\n");
term_puts_color("Built with Clang + lld\n", DA_TEXT_DIM);
}
static void cmd_clear(void) { term_clear(); }
static void cmd_reboot(void) {
term_puts_color("Rebooting...\n", DA_PROMPT_COLOR);
redraw_all();
outb(0x64, 0xFE);
for(;;) __asm__("hlt");
}
static void cmd_halt(void) {
term_puts_color("System halted.\n", DA_PROMPT_COLOR);
redraw_all();
for(;;) __asm__("hlt");
}
static void cmd_echo(const char *args) {
if (args && *args) term_puts_color(args, DA_TEXT_PRIMARY);
term_putc('\n');
}
static void cmd_date(void) {
u8 year, month, day, hour, minute, second;
outb(0x70, 9); year = inb(0x71);
outb(0x70, 8); month = inb(0x71);
outb(0x70, 7); day = inb(0x71);
outb(0x70, 4); hour = inb(0x71);
outb(0x70, 2); minute = inb(0x71);
outb(0x70, 0); second = inb(0x71);
year = (u8)((year>>4)*10+(year&0xF));
month = (u8)((month>>4)*10+(month&0xF));
day = (u8)((day>>4)*10+(day&0xF));
hour = (u8)((hour>>4)*10+(hour&0xF));
minute = (u8)((minute>>4)*10+(minute&0xF));
second = (u8)((second>>4)*10+(second&0xF));
char buf[32]; int p = 0;
buf[p++]='2'; buf[p++]='0'; buf[p++]='0'+year/10; buf[p++]='0'+year%10; buf[p++]='-';
buf[p++]='0'+month/10; buf[p++]='0'+month%10; buf[p++]='-';
buf[p++]='0'+day/10; buf[p++]='0'+day%10; buf[p++]=' ';
buf[p++]='0'+hour/10; buf[p++]='0'+hour%10; buf[p++]=':';
buf[p++]='0'+minute/10; buf[p++]='0'+minute%10; buf[p++]=':';
buf[p++]='0'+second/10; buf[p++]='0'+second%10; buf[p]=0;
term_puts(buf); term_putc('\n');
}
static void cmd_ls(void) {
if (!g_ac.block_read) {
term_puts_color("ls: 无块设备可用\n", DA_ERROR);
return;
}
/* 读取 FAT32 根目录 — 简化版:读取 BPB 获取根目录区 */
u8 bpb[512];
if (g_ac.block_read(0, 0, 1, bpb) != 0) {
term_puts_color("ls: 读取 BPB 失败\n", DA_ERROR);
return;
}
/* 检查 FAT32 签名 */
if (bpb[510] != 0x55 || bpb[511] != 0xAA) {
term_puts_color("ls: 无有效 FAT32 分区\n", DA_ERROR);
return;
}
u32 root_cluster = *(u32 *)(bpb + 44);
u16 bytes_per_sector = *(u16 *)(bpb + 11);
u8 sectors_per_cluster = bpb[13];
u32 fat_start = *(u16 *)(bpb + 14);
u32 data_start = fat_start + (*(u16 *)(bpb + 22)) * 2;
(void)bytes_per_sector;
(void)sectors_per_cluster;
(void)data_start;
/* 读取根目录簇 — 简化:只读取第一个簇 */
u8 dir_buf[8192];
u32 cluster = root_cluster;
u32 lba = data_start + (cluster - 2) * sectors_per_cluster;
if (g_ac.block_read(0, lba, sectors_per_cluster, dir_buf) != 0) {
term_puts_color("ls: 读取根目录失败\n", DA_ERROR);
return;
}
/* 解析目录项 */
int found = 0;
for (int i = 0; i < (int)(sectors_per_cluster * 512); i += 32) {
u8 first = dir_buf[i];
if (first == 0x00) break;
if (first == 0xE5) continue;
if (dir_buf[i+11] & 0x08) continue; /* 卷标 */
if (dir_buf[i+11] & 0x10) {
/* 目录 */
term_puts_color("[DIR] ", DA_ACCENT);
} else {
term_puts_color(" ", DA_TEXT_PRIMARY);
}
/* 文件名(8.3) */
for (int j = 0; j < 8; j++) {
if (dir_buf[i+j] == ' ') break;
term_putc((char)dir_buf[i+j]);
}
if (dir_buf[i+8] != ' ') {
term_putc('.');
for (int j = 8; j < 11; j++) {
if (dir_buf[i+j] == ' ') break;
term_putc((char)dir_buf[i+j]);
}
}
term_putc('\n');
found++;
}
if (found == 0) term_puts_color("(empty)\n", DA_TEXT_DIM);
}
static void cmd_cat(const char *args) {
if (!args || !*args) {
term_puts_color("cat: 需要文件名参数\n", DA_ERROR);
return;
}
if (!g_ac.block_read) {
term_puts_color("cat: 无块设备可用\n", DA_ERROR);
return;
}
term_puts_color("cat: 文件读取暂未完整实现\n", DA_WARNING);
}
static void execute_command(const char *cmd) {
while (*cmd == ' ') cmd++;
if (*cmd == 0) return;
const char *args = cmd;
while (*args && *args != ' ') args++;
char name[32];
int nlen = (int)(args - cmd);
if (nlen >= 32) nlen = 31;
for (int i = 0; i < nlen; i++) name[i] = cmd[i];
name[nlen] = 0;
while (*args == ' ') args++;
if (str_eq(name,"help")||str_eq(name,"?")) cmd_help();
else if (str_eq(name,"version")||str_eq(name,"ver")) cmd_version();
else if (str_eq(name,"uname")) cmd_uname();
else if (str_eq(name,"about")) cmd_about();
else if (str_eq(name,"clear")||str_eq(name,"cls")) cmd_clear();
else if (str_eq(name,"reboot")) cmd_reboot();
else if (str_eq(name,"halt")||str_eq(name,"shutdown")) cmd_halt();
else if (str_eq(name,"echo")) cmd_echo(args);
else if (str_eq(name,"date")||str_eq(name,"time")) cmd_date();
else if (str_eq(name,"ls")||str_eq(name,"dir")) cmd_ls();
else if (str_eq(name,"cat")) cmd_cat(args);
else if (str_eq(name,"pwd")) { term_puts("/\n"); }
else if (str_eq(name,"whoami")) { term_puts("root\n"); }
else if (str_eq(name,"id")) { term_puts("uid=0(root) gid=0(root)\n"); }
else {
term_puts_color("未知命令: ", DA_ERROR);
term_puts_color(name, DA_ERROR);
term_putc('\n');
term_puts_color("输入 'help' 查看可用命令\n", DA_TEXT_DIM);
}
}
static void add_history(const char *cmd) {
if (hist_count < HIST_SIZE) {
int i;
for (i = 0; cmd[i] && i < 255; i++) history[hist_count][i] = cmd[i];
history[hist_count][i] = 0;
hist_count++;
}
hist_idx = hist_count;
}
/* ---- 输入行重绘 ---- */
static void redraw_input_line(void) {
int plen = 0;
while (PROMPT_STR[plen]) plen++;
i64 y = term_y + (i64)cur_row * CHAR_H;
da_fill_rect(&g_ac, term_x, y, (i64)term_w * CHAR_STEP, CHAR_H, DA_BG_PRIMARY);
for (int i = 0; i < plen; i++)
da_draw_char(&g_ac, PROMPT_STR[i], term_x + i * CHAR_STEP, y, DA_PROMPT_COLOR, DA_BG_PRIMARY);
for (int i = 0; i < input_len; i++)
da_draw_char(&g_ac, input_buf[i], term_x + (plen + i) * CHAR_STEP, y, DA_TEXT_PRIMARY, DA_BG_PRIMARY);
cur_col = plen + input_cursor;
/* 光标 */
da_fill_rect(&g_ac, term_x + cur_col * CHAR_STEP, y + CHAR_H - 3, 11, 2, DA_ACCENT);
}
/* ---- 主入口 ---- */
__attribute__((visibility("default")))
void dsk_entry(const da_boot_context *ctx) {
__asm__ volatile("cli");
da_slog("bash", "boot");
if (!ctx || ctx->magic != DA_BOOT_MAGIC) {
da_slog("bash", "bad context");
for(;;) __asm__("hlt");
}
da_init(&g_ac, ctx);
term_init_layout();
term_clear();
/* 启动 banner */
term_puts_color("=== Deshab Bash v0.1 ===\n", DA_ACCENT);
term_puts_color("欢迎使用 Deshab 终端\n", DA_SUCCESS);
term_puts_color("输入 'help' 查看可用命令 | Esc 退出\n", DA_TEXT_DIM);
term_putc('\n');
redraw_all();
int shift = 0, e0 = 0;
for (;;) {
/* 绘制提示符 */
term_puts_color(PROMPT_STR, DA_PROMPT_COLOR);
input_len = 0; input_cursor = 0; input_buf[0] = 0;
int plen = 0; while (PROMPT_STR[plen]) plen++;
cur_col = plen;
redraw_all();
int cmd_done = 0;
while (!cmd_done) {
u8 st = inb(0x64);
if (!(st & 1)) { __asm__("pause"); continue; }
u8 data = inb(0x60);
if (st & 0x20) continue;
u8 sc = data;
if (sc == 0xE0) { e0 = 1; continue; }
if (sc == 0x2A || sc == 0x36) { shift = 1; continue; }
if (sc == 0xAA || sc == 0xB6) { shift = 0; continue; }
if (sc & 0x80) { e0 = 0; continue; }
/* Esc 退出 */
if (sc == 0x01 && !e0) {
da_slog("bash", "exit");
return;
}
if (e0) {
if (sc == 0x4B) { if (input_cursor > 0) input_cursor--; }
else if (sc == 0x4D) { if (input_cursor < input_len) input_cursor++; }
else if (sc == 0x47) { input_cursor = 0; }
else if (sc == 0x4F) { input_cursor = input_len; }
else if (sc == 0x48) { /* 上箭头 - 历史 */
if (hist_idx > 0) {
hist_idx--;
int i; for (i = 0; history[hist_idx][i] && i < 255; i++) input_buf[i] = history[hist_idx][i];
input_len = i; input_cursor = i; input_buf[i] = 0;
}
}
else if (sc == 0x50) { /* 下箭头 - 历史 */
if (hist_idx < hist_count - 1) {
hist_idx++;
int i; for (i = 0; history[hist_idx][i] && i < 255; i++) input_buf[i] = history[hist_idx][i];
input_len = i; input_cursor = i; input_buf[i] = 0;
} else if (hist_idx < hist_count) {
hist_idx = hist_count;
input_len = 0; input_cursor = 0; input_buf[0] = 0;
}
}
e0 = 0;
redraw_input_line();
continue;
}
if (sc == 0x1C) { /* Enter */
input_buf[input_len] = 0;
term_putc('\n');
if (input_len > 0) add_history(input_buf);
execute_command(input_buf);
cmd_done = 1;
continue;
}
if (sc == 0x0E) { /* Backspace */
if (input_cursor > 0) {
for (int i = input_cursor - 1; i < input_len - 1; i++)
input_buf[i] = input_buf[i+1];
input_len--; input_cursor--;
input_buf[input_len] = 0;
}
redraw_input_line();
continue;
}
char c = da_scan_to_ascii(sc, shift);
if (c && c >= 32 && c <= 126 && input_len < 255) {
for (int i = input_len; i > input_cursor; i--)
input_buf[i] = input_buf[i-1];
input_buf[input_cursor++] = c;
input_len++;
redraw_input_line();
}
}
redraw_all();
}
}
+25
View File
@@ -0,0 +1,25 @@
CC := clang
LD := ld.lld
ROOT := ../../..
OUT := $(ROOT)/SYSTEM/system/deshab64/tools/browser.elf
CFLAGS := -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.. -I../../UTSM/include -O2
LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld
C_SOURCES := main.c
OBJECTS := $(C_SOURCES:.c=.o)
.PHONY: all clean
all: $(OUT)
$(OUT): $(OBJECTS) linker.ld
@if not exist "$(ROOT)\SYSTEM\system\deshab64\tools" mkdir "$(ROOT)\SYSTEM\system\deshab64\tools"
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }"
+16
View File
@@ -0,0 +1,16 @@
ENTRY(dsk_entry)
PHDRS
{
text PT_LOAD FLAGS(7);
rodata PT_LOAD FLAGS(4);
data PT_LOAD FLAGS(7);
}
SECTIONS
{
. = 0x1000;
.text : ALIGN(4K) { *(.text*) } :text
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
.data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data
}
+128
View File
@@ -0,0 +1,128 @@
/* Deshab Browser — 全屏浏览器 (Stub)
* 地址栏 + 导航按钮 + 内容渲染区域。
* 当前显示 stub 提示,Esc 退出。
*/
#include "../../firstInit/ascii_bitmaps.c"
#include "../desktop_app.h"
#define CHAR_STEP 12
#define CHAR_H 18
static da_app_context g_ac;
static da_cursor g_cursor;
static da_mouse g_mouse;
static void redraw_all(void) {
da_fill_bg(&g_ac, DA_BG_PRIMARY);
/* 标题栏 */
da_draw_titlebar(&g_ac, "Browser", (i64)g_ac.fb_w);
/* 地址栏 */
int bar_y = DA_TITLEBAR_H + 6;
int bar_h = 28;
int bar_x = 12;
int bar_w = (int)g_ac.fb_w - 24;
da_fill_rounded_rect(&g_ac, bar_x, bar_y, bar_w, bar_h, DA_BG_TERTIARY, 6);
da_rect_outline(&g_ac, bar_x, bar_y, bar_w, bar_h, DA_BORDER, 6);
/* 导航按钮 */
int btn_w = 32;
da_fill_rounded_rect(&g_ac, bar_x + 4, bar_y + 4, btn_w, bar_h - 8, DA_BG_SECONDARY, 4);
da_draw_string(&g_ac, "<", bar_x + 14, bar_y + 5, DA_TEXT_PRIMARY, DA_BG_SECONDARY, CHAR_STEP);
da_fill_rounded_rect(&g_ac, bar_x + 4 + btn_w + 4, bar_y + 4, btn_w, bar_h - 8, DA_BG_SECONDARY, 4);
da_draw_string(&g_ac, ">", bar_x + 4 + btn_w + 4 + 12, bar_y + 5, DA_TEXT_PRIMARY, DA_BG_SECONDARY, CHAR_STEP);
da_fill_rounded_rect(&g_ac, bar_x + 4 + (btn_w + 4) * 2, bar_y + 4, btn_w, bar_h - 8, DA_BG_SECONDARY, 4);
da_draw_string(&g_ac, "R", bar_x + 4 + (btn_w + 4) * 2 + 12, bar_y + 5, DA_TEXT_PRIMARY, DA_BG_SECONDARY, CHAR_STEP);
/* URL 文本 */
int url_x = bar_x + (btn_w + 4) * 3 + 8;
da_draw_string(&g_ac, "deshab://home", url_x, bar_y + 5, DA_TEXT_DIM, DA_BG_TERTIARY, CHAR_STEP);
/* 内容区域 */
int content_y = bar_y + bar_h + 12;
int content_x = 16;
int content_w = (int)g_ac.fb_w - 32;
/* Stub 页面 */
da_fill_rounded_rect(&g_ac, content_x, content_y, content_w, 200, DA_BG_SECONDARY, 8);
da_rect_outline(&g_ac, content_x, content_y, content_w, 200, DA_BORDER, 8);
/* 居中图标 */
int icon_cx = content_x + content_w / 2 - 32;
int icon_cy = content_y + 24;
da_fill_rounded_rect(&g_ac, icon_cx, icon_cy, 64, 48, DA_BG_TERTIARY, 8);
/* 地球图标简化:圆圈 */
int cx = icon_cx + 32, cy = icon_cy + 24, r = 16;
for (int dy = -r; dy <= r; dy++)
for (int dx = -r; dx <= r; dx++)
if (dx*dx+dy*dy <= r*r && dx*dx+dy*dy > (r-1)*(r-1))
da_pixel(&g_ac, cx+dx, cy+dy, DA_ACCENT);
/* 横线 */
for (int dx = -r; dx <= r; dx++)
if (dx*dx + 0 <= r*r)
da_pixel(&g_ac, cx+dx, cy, DA_ACCENT);
/* 竖线 */
for (int dy = -r; dy <= r; dy++)
if (0 + dy*dy <= r*r)
da_pixel(&g_ac, cx, cy+dy, DA_ACCENT);
/* 提示文字 */
da_draw_string(&g_ac, "Deshab Browser",
content_x + content_w / 2 - 7 * CHAR_STEP, icon_cy + 60,
DA_TEXT_PRIMARY, DA_BG_SECONDARY, CHAR_STEP);
da_draw_string(&g_ac, "HTTP + HTML rendering required",
content_x + content_w / 2 - 15 * CHAR_STEP, icon_cy + 82,
DA_TEXT_DIM, DA_BG_SECONDARY, CHAR_STEP);
da_draw_string(&g_ac, "Press Esc to exit",
content_x + content_w / 2 - 9 * CHAR_STEP, icon_cy + 104,
DA_TEXT_DIM, DA_BG_SECONDARY, CHAR_STEP);
/* 状态栏 */
da_draw_statusbar(&g_ac, "Browser | Stub - No network stack", (i64)g_ac.fb_w, (i64)g_ac.fb_h);
/* 鼠标光标 */
da_cursor_save(&g_ac, &g_cursor);
da_cursor_draw(&g_ac, &g_cursor, DA_CURSOR_COLOR);
}
__attribute__((visibility("default")))
void dsk_entry(const da_boot_context *ctx) {
__asm__ volatile("cli");
da_slog("browser", "boot");
if (!ctx || ctx->magic != DA_BOOT_MAGIC) {
da_slog("browser", "bad context");
for(;;) __asm__("hlt");
}
da_init(&g_ac, ctx);
da_cursor_init(&g_cursor, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
da_mouse_init();
redraw_all();
for (;;) {
int need_redraw = 0;
/* 鼠标轮询 */
if (da_mouse_poll(&g_mouse, &g_cursor, (i64)g_ac.fb_w, (i64)g_ac.fb_h))
need_redraw = 1;
/* 键盘轮询 */
u8 st = inb(0x64);
if ((st & 1) && !(st & 0x20)) {
u8 data = inb(0x60);
if (!(data & 0x80) && data == 0x01) {
da_slog("browser", "exit");
return;
}
}
if (need_redraw) {
da_cursor_restore(&g_ac, &g_cursor);
redraw_all();
}
__asm__("pause");
}
}
+25
View File
@@ -0,0 +1,25 @@
CC := clang
LD := ld.lld
ROOT := ../../..
OUT := $(ROOT)/SYSTEM/system/deshab64/tools/curl.elf
CFLAGS := -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.. -I../../UTSM/include -O2
LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld
C_SOURCES := main.c
OBJECTS := $(C_SOURCES:.c=.o)
.PHONY: all clean
all: $(OUT)
$(OUT): $(OBJECTS) linker.ld
@if not exist "$(ROOT)\SYSTEM\system\deshab64\tools" mkdir "$(ROOT)\SYSTEM\system\deshab64\tools"
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }"
+16
View File
@@ -0,0 +1,16 @@
ENTRY(dsk_entry)
PHDRS
{
text PT_LOAD FLAGS(7);
rodata PT_LOAD FLAGS(4);
data PT_LOAD FLAGS(7);
}
SECTIONS
{
. = 0x1000;
.text : ALIGN(4K) { *(.text*) } :text
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
.data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data
}
+324
View File
@@ -0,0 +1,324 @@
/* Deshab curl — 终端风格 HTTP 客户端 stub
* 命令行输入 URL,显示 HTTP GET 请求结果。
* 当前为 stub,提示需要 TCP/IP 栈支持。
* Esc 键退出返回 DSK。
*/
#include "../../firstInit/ascii_bitmaps.c"
#include "../desktop_app.h"
/* ---- 终端参数 ---- */
#define TERM_COLS 120
#define TERM_ROWS 40
#define TERM_MAX_CHARS (TERM_COLS * TERM_ROWS)
#define CHAR_STEP 12
#define CHAR_H 18
#define PROMPT_STR "curl> "
static da_app_context g_ac;
/* 终端缓冲区 */
static u8 term_ch[TERM_MAX_CHARS];
static u32 term_fg[TERM_MAX_CHARS];
static int term_w = TERM_COLS;
static int term_h = TERM_ROWS;
static int cur_col = 0;
static int cur_row = 0;
/* 输入缓冲 */
static char input_buf[256];
static int input_len = 0;
static int input_cursor = 0;
/* 命令历史 */
#define HIST_SIZE 16
static char history[HIST_SIZE][256];
static int hist_count = 0;
static int hist_idx = 0;
/* 坐标 */
static int term_x, term_y;
static void term_init_layout(void) {
term_x = 8;
term_y = DA_TITLEBAR_H + 4;
int avail_w = (int)g_ac.fb_w - 16;
int avail_h = (int)g_ac.fb_h - term_y - DA_STATUSBAR_H - 8;
term_w = avail_w / CHAR_STEP;
term_h = avail_h / CHAR_H;
if (term_w < 10) term_w = 10;
if (term_h < 5) term_h = 5;
if (term_w > TERM_COLS) term_w = TERM_COLS;
if (term_h > TERM_ROWS) term_h = TERM_ROWS;
}
static void term_clear(void) {
for (int i = 0; i < TERM_MAX_CHARS; i++) {
term_ch[i] = ' ';
term_fg[i] = DA_TEXT_PRIMARY;
}
cur_col = 0; cur_row = 0;
}
static void term_scroll(void) {
for (int r = 1; r < term_h; r++) {
for (int c = 0; c < term_w; c++) {
term_ch[(r-1)*TERM_COLS+c] = term_ch[r*TERM_COLS+c];
term_fg[(r-1)*TERM_COLS+c] = term_fg[r*TERM_COLS+c];
}
}
for (int c = 0; c < term_w; c++) {
term_ch[(term_h-1)*TERM_COLS+c] = ' ';
term_fg[(term_h-1)*TERM_COLS+c] = DA_TEXT_PRIMARY;
}
cur_row = term_h - 1; cur_col = 0;
}
static void term_newline(void) {
cur_col = 0; cur_row++;
if (cur_row >= term_h) term_scroll();
}
static void term_putc_color(char c, u32 color) {
if (c == '\n') { term_newline(); return; }
if (c == '\r') { cur_col = 0; return; }
if (c == '\t') { int sp = 4 - (cur_col % 4); for (int i = 0; i < sp; i++) term_putc_color(' ', color); return; }
if (c < ' ' || c > '~') c = '?';
if (cur_col >= term_w) term_newline();
int idx = cur_row * TERM_COLS + cur_col;
term_ch[idx] = (u8)c; term_fg[idx] = color;
cur_col++;
}
static void term_putc(char c) { term_putc_color(c, DA_TEXT_PRIMARY); }
static void term_puts_color(const char *s, u32 color) { while (*s) term_putc_color(*s++, color); }
static void term_puts(const char *s) { term_puts_color(s, DA_TEXT_PRIMARY); }
/* ---- 绘制 ---- */
static void redraw_all(void) {
da_fill_bg(&g_ac, DA_BG_PRIMARY);
da_draw_titlebar(&g_ac, "curl", (i64)g_ac.fb_w);
/* 终端字符 */
for (int r = 0; r < term_h; r++) {
for (int c = 0; c < term_w; c++) {
int idx = r * TERM_COLS + c;
if (term_ch[idx] == ' ') continue;
da_draw_char(&g_ac, term_ch[idx],
term_x + c * CHAR_STEP, term_y + r * CHAR_H,
term_fg[idx], DA_BG_PRIMARY);
}
}
/* 光标 */
if (cur_col < term_w && cur_row < term_h) {
da_fill_rect(&g_ac, term_x + cur_col * CHAR_STEP,
term_y + cur_row * CHAR_H + CHAR_H - 3, 11, 2, DA_ACCENT);
}
/* 状态栏 */
char status[64];
int p = 0;
const char *s1 = "HTTP client | Esc 退出 ";
while (*s1) status[p++] = *s1++;
u8 hh, mm; da_rtc_time(&hh, &mm);
status[p++] = '0' + hh/10; status[p++] = '0' + hh%10;
status[p++] = ':'; status[p++] = '0' + mm/10; status[p++] = '0' + mm%10;
status[p] = 0;
da_draw_statusbar(&g_ac, status, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
}
/* ---- URL 请求模拟 ---- */
static void do_http_get(const char *url) {
term_puts_color("GET ", DA_PROMPT_COLOR);
term_puts_color(url, DA_ACCENT_LIGHT);
term_putc('\n');
term_puts_color(" -> 解析主机...\n", DA_TEXT_DIM);
term_puts_color(" -> 建立 TCP 连接...\n", DA_TEXT_DIM);
/* stub: 检查 kernel_api 是否有 net 接口 */
if (!g_ac.kernel_api) {
term_puts_color(" [错误] 无内核 API\n", DA_ERROR);
return;
}
/* 尝试读取 net API — 当前为 stub */
u64 net_api = *(u64 *)((u64)g_ac.kernel_api + 0xC0);
if (!net_api) {
term_puts_color(" [错误] 需要 TCP/IP 栈支持\n", DA_ERROR);
term_puts_color(" 当前内核未实现网络协议栈。\n", DA_WARNING);
term_puts_color(" 请等待后续版本更新。\n", DA_TEXT_DIM);
return;
}
/* 如果有 net API,此处应实现真正的 HTTP GET */
term_puts_color(" [stub] 网络功能尚未实现\n", DA_WARNING);
}
/* ---- 内置命令 ---- */
static int str_eq(const char *a, const char *b) {
while (*a && *b) { if (*a != *b) return 0; a++; b++; }
return *a == *b;
}
static void cmd_help(void) {
term_puts_color("curl — 命令行 HTTP 客户端\n", DA_PROMPT_COLOR);
term_puts(" <url> 发送 HTTP GET 请求\n");
term_puts(" help 显示此帮助\n");
term_puts(" clear 清屏\n");
term_puts(" version 显示版本\n");
term_puts(" Esc 退出\n");
}
static void cmd_clear(void) { term_clear(); }
static void cmd_version(void) {
term_puts_color("Deshab curl v0.1 (stub)\n", DA_SUCCESS);
term_puts_color(" HTTP 客户端功能需要 TCP/IP 栈支持\n", DA_TEXT_DIM);
}
static void execute_command(const char *cmd) {
while (*cmd == ' ') cmd++;
if (*cmd == 0) return;
if (str_eq(cmd, "help") || str_eq(cmd, "?")) { cmd_help(); return; }
if (str_eq(cmd, "clear") || str_eq(cmd, "cls")) { cmd_clear(); return; }
if (str_eq(cmd, "version") || str_eq(cmd, "ver")) { cmd_version(); return; }
/* 非内置命令视为 URL */
if (cmd[0] == 'h' && cmd[1] == 't' && cmd[2] == 't' && cmd[3] == 'p') {
do_http_get(cmd);
} else {
term_puts_color("无效 URL: ", DA_ERROR);
term_puts_color(cmd, DA_ERROR);
term_putc('\n');
term_puts_color("请输入完整 URL (http://...)\n", DA_TEXT_DIM);
}
}
static void add_history(const char *cmd) {
if (hist_count < HIST_SIZE) {
int i;
for (i = 0; cmd[i] && i < 255; i++) history[hist_count][i] = cmd[i];
history[hist_count][i] = 0;
hist_count++;
}
hist_idx = hist_count;
}
/* ---- 输入行重绘 ---- */
static void redraw_input_line(void) {
int plen = 0;
while (PROMPT_STR[plen]) plen++;
i64 y = term_y + (i64)cur_row * CHAR_H;
da_fill_rect(&g_ac, term_x, y, (i64)term_w * CHAR_STEP, CHAR_H, DA_BG_PRIMARY);
for (int i = 0; i < plen; i++)
da_draw_char(&g_ac, PROMPT_STR[i], term_x + i * CHAR_STEP, y, DA_PROMPT_COLOR, DA_BG_PRIMARY);
for (int i = 0; i < input_len; i++)
da_draw_char(&g_ac, input_buf[i], term_x + (plen + i) * CHAR_STEP, y, DA_TEXT_PRIMARY, DA_BG_PRIMARY);
cur_col = plen + input_cursor;
da_fill_rect(&g_ac, term_x + cur_col * CHAR_STEP, y + CHAR_H - 3, 11, 2, DA_ACCENT);
}
/* ---- 主入口 ---- */
__attribute__((visibility("default")))
void dsk_entry(const da_boot_context *ctx) {
__asm__ volatile("cli");
da_slog("curl", "boot");
if (!ctx || ctx->magic != DA_BOOT_MAGIC) {
da_slog("curl", "bad context");
for(;;) __asm__("hlt");
}
da_init(&g_ac, ctx);
term_init_layout();
term_clear();
/* 启动 banner */
term_puts_color("=== Deshab curl v0.1 (stub) ===\n", DA_ACCENT);
term_puts_color("HTTP 命令行客户端\n", DA_SUCCESS);
term_puts_color("输入 URL 发送 GET 请求 | 输入 'help' 查看帮助 | Esc 退出\n", DA_TEXT_DIM);
term_puts_color("注意: 当前需要 TCP/IP 栈支持\n", DA_WARNING);
term_putc('\n');
redraw_all();
int shift = 0, e0 = 0;
for (;;) {
term_puts_color(PROMPT_STR, DA_PROMPT_COLOR);
input_len = 0; input_cursor = 0; input_buf[0] = 0;
int plen = 0; while (PROMPT_STR[plen]) plen++;
cur_col = plen;
redraw_all();
int cmd_done = 0;
while (!cmd_done) {
u8 st = inb(0x64);
if (!(st & 1)) { __asm__("pause"); continue; }
u8 data = inb(0x60);
if (st & 0x20) continue;
u8 sc = data;
if (sc == 0xE0) { e0 = 1; continue; }
if (sc == 0x2A || sc == 0x36) { shift = 1; continue; }
if (sc == 0xAA || sc == 0xB6) { shift = 0; continue; }
if (sc & 0x80) { e0 = 0; continue; }
/* Esc 退出 */
if (sc == 0x01 && !e0) {
da_slog("curl", "exit");
return;
}
if (e0) {
if (sc == 0x4B) { if (input_cursor > 0) input_cursor--; }
else if (sc == 0x4D) { if (input_cursor < input_len) input_cursor++; }
else if (sc == 0x47) { input_cursor = 0; }
else if (sc == 0x4F) { input_cursor = input_len; }
else if (sc == 0x48) {
if (hist_idx > 0) {
hist_idx--;
int i; for (i = 0; history[hist_idx][i] && i < 255; i++) input_buf[i] = history[hist_idx][i];
input_len = i; input_cursor = i; input_buf[i] = 0;
}
}
else if (sc == 0x50) {
if (hist_idx < hist_count - 1) {
hist_idx++;
int i; for (i = 0; history[hist_idx][i] && i < 255; i++) input_buf[i] = history[hist_idx][i];
input_len = i; input_cursor = i; input_buf[i] = 0;
} else if (hist_idx < hist_count) {
hist_idx = hist_count;
input_len = 0; input_cursor = 0; input_buf[0] = 0;
}
}
e0 = 0;
redraw_input_line();
continue;
}
if (sc == 0x1C) { /* Enter */
input_buf[input_len] = 0;
term_putc('\n');
if (input_len > 0) add_history(input_buf);
execute_command(input_buf);
cmd_done = 1;
continue;
}
if (sc == 0x0E) { /* Backspace */
if (input_cursor > 0) {
for (int i = input_cursor - 1; i < input_len - 1; i++)
input_buf[i] = input_buf[i+1];
input_len--; input_cursor--;
input_buf[input_len] = 0;
}
redraw_input_line();
continue;
}
char c = da_scan_to_ascii(sc, shift);
if (c && c >= 32 && c <= 126 && input_len < 255) {
for (int i = input_len; i > input_cursor; i--)
input_buf[i] = input_buf[i-1];
input_buf[input_cursor++] = c;
input_len++;
redraw_input_line();
}
}
redraw_all();
}
}
+374
View File
@@ -0,0 +1,374 @@
/* desktop_app.h — Deshab 独立工具应用公共头文件
*
* 独立工具应用运行在 DSK 加载的 .elf 中,全屏独占 framebuffer。
* 每个 .elf 实现 dsk_entry(dsk_boot_context *ctx) 入口点。
* 通过 kernel_api 访问块设备、网络等内核服务。
* PS/2 键盘/鼠标通过端口 I/O 直接轮询。
*/
#ifndef DESHAB_DESKTOP_APP_H
#define DESHAB_DESKTOP_APP_H
/* 基础类型 */
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef long long i64;
/* 端口 I/O */
static inline void outb(u16 port, u8 val) { __asm__ volatile("outb %0,%1"::"a"(val),"Nd"(port)); }
static inline u8 inb(u16 port) { u8 v; __asm__ volatile("inb %1,%0":"=a"(v):"Nd"(port)); return v; }
/* COM1 串口日志 */
#define DA_COM1 0x3F8
static inline void da_sputc(char c) { for(unsigned i=0;i<100000;i++){if(inb(DA_COM1+5)&0x20)break;} outb(DA_COM1,(u8)c); }
static inline void da_swrite(const char *s) { while(*s){if(*s=='\n')da_sputc('\r');da_sputc(*s++);} }
static inline void da_slog(const char *tag, const char *msg) { da_swrite("["); da_swrite(tag); da_swrite("] "); da_swrite(msg); da_swrite("\n"); }
/* dsk_boot_context 定义(与 dsk.h 一致,自包含避免依赖) */
#define DA_BOOT_MAGIC 0x44534B31424F4F54ULL
typedef struct {
u64 magic; u32 abi_version; u32 size; u64 flags;
u64 hhdm_offset; u64 rsdp_address;
u64 framebuffer_address; u64 framebuffer_width; u64 framebuffer_height;
u64 framebuffer_pitch; u32 framebuffer_bpp; u32 reserved_fb;
u64 boot_modules_response;
u64 dkm_kernel_api; u64 dkm_driver_table; u64 dkm_driver_count;
u64 utsm_state; u64 drr_state;
u64 memory_map; u64 memory_map_count; u64 memory_map_entry_size;
u64 kernel_stack_top; u64 reserved[8];
} da_boot_context;
/* block_read 函数类型 */
typedef int (*da_block_read_fn)(void *ctx, u64 lba, u32 count, void *buffer);
/* 应用上下文 — 从 dsk_boot_context 提取的常用数据 */
typedef struct {
u32 *fb; /* framebuffer 基址 */
u64 fb_w; /* 宽度 */
u64 fb_h; /* 高度 */
u64 fb_pitch; /* 行宽(字节) */
da_block_read_fn block_read;
const void *kernel_api;
} da_app_context;
/* 从 dsk_boot_context 初始化 da_app_context */
static inline void da_init(da_app_context *ac, const da_boot_context *ctx) {
ac->fb = (u32 *)ctx->framebuffer_address;
ac->fb_w = ctx->framebuffer_width;
ac->fb_h = ctx->framebuffer_height;
ac->fb_pitch = ctx->framebuffer_pitch;
ac->kernel_api = (const void *)ctx->dkm_kernel_api;
/* block_read 从 kernel_api + 0xA8 获取 */
u64 api = ctx->dkm_kernel_api;
ac->block_read = (da_block_read_fn)*(u64 *)(api + 0xA8);
if (ac->block_read)
ac->block_read = (da_block_read_fn)*(u64 *)((u64)ac->block_read + 16);
}
/* ========== 帧缓冲绘制 ========== */
static inline void da_pixel(da_app_context *ac, i64 x, i64 y, u32 color) {
if (x < 0 || (u64)x >= ac->fb_w || y < 0 || (u64)y >= ac->fb_h) return;
u32 *line = (u32 *)((u8 *)ac->fb + (u64)y * ac->fb_pitch);
line[x] = color;
}
static inline u32 da_pixel_read(da_app_context *ac, i64 x, i64 y) {
if (x < 0 || (u64)x >= ac->fb_w || y < 0 || (u64)y >= ac->fb_h) return 0;
u32 *line = (u32 *)((u8 *)ac->fb + (u64)y * ac->fb_pitch);
return line[x];
}
static inline u32 da_blend(u32 bg, u32 fg, u32 alpha) {
u32 na = 256 - alpha;
u32 r = ((bg & 0xFF) * na + (fg & 0xFF) * alpha) >> 8;
u32 g = (((bg >> 8) & 0xFF) * na + ((fg >> 8) & 0xFF) * alpha) >> 8;
u32 b = (((bg >> 16) & 0xFF) * na + ((fg >> 16) & 0xFF) * alpha) >> 8;
return 0xFF000000u | (b << 16) | (g << 8) | r;
}
static inline void da_fill_rect(da_app_context *ac, i64 x, i64 y, i64 w, i64 h, u32 color) {
for (i64 r = 0; r < h; r++) {
i64 yy = y + r;
if (yy < 0 || (u64)yy >= ac->fb_h) continue;
u32 *line = (u32 *)((u8 *)ac->fb + (u64)yy * ac->fb_pitch);
for (i64 c = 0; c < w; c++) {
i64 xx = x + c;
if (xx < 0 || (u64)xx >= ac->fb_w) continue;
line[xx] = color;
}
}
}
static inline void da_fill_bg(da_app_context *ac, u32 color) {
da_fill_rect(ac, 0, 0, (i64)ac->fb_w, (i64)ac->fb_h, color);
}
/* 圆角矩形 */
static inline void da_fill_rounded_rect(da_app_context *ac, i64 x, i64 y, i64 w, i64 h, u32 color, i64 radius) {
i64 r = radius;
if (r > w/2) r = w/2;
if (r > h/2) r = h/2;
da_fill_rect(ac, x+r, y, w-2*r, h, color);
da_fill_rect(ac, x, y+r, r, h-2*r, color);
da_fill_rect(ac, x+w-r, y+r, r, h-2*r, color);
for (i64 dy = 0; dy < r; dy++)
for (i64 dx = 0; dx < r; dx++)
if (dx*dx+dy*dy <= r*r) {
da_pixel(ac, x+r-1-dx, y+r-1-dy, color);
da_pixel(ac, x+w-r+dx, y+r-1-dy, color);
da_pixel(ac, x+r-1-dx, y+h-r+dy, color);
da_pixel(ac, x+w-r+dx, y+h-r+dy, color);
}
}
static inline void da_rect_outline(da_app_context *ac, i64 x, i64 y, i64 w, i64 h, u32 color, i64 radius) {
i64 r = radius;
if (r > w/2) r = w/2;
if (r > h/2) r = h/2;
for (i64 c = r; c < w-r; c++) { da_pixel(ac, x+c, y, color); da_pixel(ac, x+c, y+h-1, color); }
for (i64 rr = r; rr < h-r; rr++) { da_pixel(ac, x, y+rr, color); da_pixel(ac, x+w-1, y+rr, color); }
for (i64 dy = 0; dy <= r; dy++)
for (i64 dx = 0; dx <= r; dx++) {
i64 d2 = dx*dx+dy*dy;
if (d2 <= r*r && d2 > (r-1)*(r-1)) {
da_pixel(ac, x+r-dx, y+r-dy, color);
da_pixel(ac, x+w-1-r+dx, y+r-dy, color);
da_pixel(ac, x+r-dx, y+h-1-r+dy, color);
da_pixel(ac, x+w-1-r+dx, y+h-1-r+dy, color);
}
}
}
/* ========== ASCII 字体渲染 ==========
*
* 使用方法:先 #include ascii_bitmaps.c(定义 static g_ascii/g_ascii_w/g_ascii_h),
* 再 #include desktop_app.h。da_draw_char/da_draw_string 直接引用 g_ascii。
* 与 deshab_ui.h 使用相同的包含顺序约定。
*/
static inline void da_draw_char(da_app_context *ac, char ch, i64 x, i64 y, u32 fg, u32 bg) {
u32 idx = (u32)(ch - ' ');
if (idx > 94) idx = 0;
const u8 *glyph = g_ascii[idx];
i64 gw = g_ascii_w, gh = g_ascii_h;
for (i64 r = 0; r < gh; r++) {
i64 yy = y + r;
if (yy < 0 || (u64)yy >= ac->fb_h) continue;
u32 *line = (u32 *)((u8 *)ac->fb + (u64)yy * ac->fb_pitch);
for (i64 c = 0; c < gw; c++) {
u8 a = glyph[r * gw + c];
if (a == 0) continue;
i64 xx = x + c;
if (xx < 0 || (u64)xx >= ac->fb_w) continue;
line[xx] = (a == 255) ? fg : da_blend(bg, fg, a);
}
}
}
static inline void da_draw_string(da_app_context *ac, const char *s, i64 x, i64 y, u32 fg, u32 bg, i64 step) {
i64 cx = x;
while (*s) { da_draw_char(ac, *s, cx, y, fg, bg); cx += step; s++; }
}
/* ========== 鼠标光标 ========== */
#define DA_CURSOR_SIZE 24
static const u8 da_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,0,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},
};
typedef struct {
u32 save[DA_CURSOR_SIZE * DA_CURSOR_SIZE];
int saved;
int old_x, old_y;
int mx, my;
int btn;
} da_cursor;
static inline void da_cursor_init(da_cursor *c, i64 fb_w, i64 fb_h) {
for (int i = 0; i < DA_CURSOR_SIZE*DA_CURSOR_SIZE; i++) c->save[i] = 0;
c->saved = 0; c->old_x = -1; c->old_y = -1;
c->mx = (int)(fb_w / 2); c->my = (int)(fb_h / 2); c->btn = 0;
}
static inline void da_cursor_save(da_app_context *ac, da_cursor *c) {
for (int r = 0; r < DA_CURSOR_SIZE; r++)
for (int col = 0; col < DA_CURSOR_SIZE; col++) {
int x = c->mx + col, y = c->my + r;
c->save[r * DA_CURSOR_SIZE + col] = da_pixel_read(ac, x, y);
}
c->saved = 1; c->old_x = c->mx; c->old_y = c->my;
}
static inline void da_cursor_restore(da_app_context *ac, da_cursor *c) {
if (!c->saved) return;
for (int r = 0; r < DA_CURSOR_SIZE; r++)
for (int col = 0; col < DA_CURSOR_SIZE; col++) {
int x = c->old_x + col, y = c->old_y + r;
da_pixel(ac, x, y, c->save[r * DA_CURSOR_SIZE + col]);
}
c->saved = 0;
}
static inline void da_cursor_draw(da_app_context *ac, da_cursor *c, u32 color) {
for (int r = 0; r < DA_CURSOR_SIZE; r++)
for (int col = 0; col < DA_CURSOR_SIZE; col++)
if (da_cursor_shape[r][col])
da_pixel(ac, c->mx + col, c->my + r, color);
}
/* ========== PS/2 鼠标 ========== */
typedef struct {
u8 buf[3];
int idx;
int has_pkt;
} da_mouse;
static inline void da_mouse_init(void) {
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
outb(0x64, 0xA8);
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; cfg &= ~0x20;
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);
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);
}
static inline int da_mouse_poll(da_mouse *m, da_cursor *c, i64 fb_w, i64 fb_h) {
u8 st = inb(0x64);
if (!(st & 1)) return 0;
u8 data = inb(0x60);
if (!(st & 0x20)) return 0;
m->buf[m->idx++] = data;
if (m->idx < 3) return 0;
m->idx = 0;
if (!(m->buf[0] & 0x08)) return 0;
int dx = (int)(signed char)m->buf[1];
int dy = (int)(signed char)m->buf[2];
if (m->buf[0] & 0x10) dx = -dx;
if (!(m->buf[0] & 0x20)) dy = -dy; else dy = -dy;
dy = -dy;
c->mx += dx; c->my += dy;
if (c->mx < 0) c->mx = 0;
if (c->my < 0) c->my = 0;
if (c->mx >= (int)fb_w - DA_CURSOR_SIZE) c->mx = (int)fb_w - DA_CURSOR_SIZE;
if (c->my >= (int)fb_h - DA_CURSOR_SIZE) c->my = (int)fb_h - DA_CURSOR_SIZE;
c->btn = (m->buf[0] & 0x01) ? 1 : 0;
m->has_pkt = 1;
return 1;
}
/* ========== 键盘 ========== */
static inline char da_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];
}
/* ========== 窗口概念 ========== */
/* 独立工具全屏运行时的窗口装饰:标题栏 + 关闭按钮 */
#define DA_TITLEBAR_H 28
#define DA_BORDER_W 2
#define DA_STATUSBAR_H 20
/* 色板引用(与 deshab_ui.h 一致) */
#define DA_BG_PRIMARY 0xFF0A1428u /* DP_ABYSS_900 */
#define DA_BG_SECONDARY 0xFF0F1E38u /* DP_ABYSS_800 */
#define DA_BG_TERTIARY 0xFF162848u /* DP_ABYSS_700 */
#define DA_TEXT_PRIMARY 0xFFE8E8F0u /* DP_NEUTRAL_700 */
#define DA_TEXT_DIM 0xFF6A6A88u /* DP_NEUTRAL_300 */
#define DA_ACCENT 0xFF00A8CCu /* DP_SEAL_500 */
#define DA_ACCENT_LIGHT 0xFF44CCF0u /* DP_SEAL_300 */
#define DA_BORDER 0xFF284888u /* DP_ABYSS_500 */
#define DA_BORDER_FOCUS 0xFF44CCF0u /* DP_SEAL_300 */
#define DA_CURSOR_COLOR 0xFF00A8CCu /* DP_SEAL_500 */
#define DA_PROMPT_COLOR 0xFF44CCF0u /* DP_SEAL_300 */
#define DA_SUCCESS 0xFF40C880u /* DP_SUCCESS */
#define DA_ERROR 0xFFFF4466u /* DP_ERROR */
#define DA_WARNING 0xFFF0A030u /* DP_WARNING */
static inline void da_draw_titlebar(da_app_context *ac, const char *title, i64 w) {
da_fill_rect(ac, 0, 0, w, DA_TITLEBAR_H, DA_BG_SECONDARY);
da_draw_string(ac, title, 8, (DA_TITLEBAR_H - 18) / 2, DA_TEXT_PRIMARY, DA_BG_SECONDARY, 12);
/* 关闭按钮 */
da_fill_rounded_rect(ac, w - 28, 4, 24, 20, DA_ERROR, 4);
da_draw_string(ac, "X", w - 20, 5, 0xFF0A1428u, DA_ERROR, 12);
/* 底部分隔线 */
da_fill_rect(ac, 0, DA_TITLEBAR_H, w, 1, DA_ACCENT);
}
static inline void da_draw_statusbar(da_app_context *ac, const char *text, i64 w, i64 h) {
i64 y = h - DA_STATUSBAR_H;
da_fill_rect(ac, 0, y, w, DA_STATUSBAR_H, DA_BG_SECONDARY);
da_draw_string(ac, text, 4, y + 1, DA_TEXT_DIM, DA_BG_SECONDARY, 12);
/* 顶部分隔线 */
da_fill_rect(ac, 0, y, w, 1, DA_BORDER);
}
/* ========== RTC 时钟 ========== */
static inline void da_rtc_time(u8 *h, u8 *m) {
outb(0x70, 4); *h = inb(0x71);
outb(0x70, 2); *m = inb(0x71);
*h = (u8)((*h >> 4) * 10 + (*h & 0xF));
*m = (u8)((*m >> 4) * 10 + (*m & 0xF));
}
/* ========== memset/memcpy ========== */
static inline void *da_memset(void *d, int c, u64 n) { u8 *p=(u8*)d; while(n--)*p++=(u8)c; return d; }
static inline void *da_memcpy(void *d, const void *s, u64 n) { u8 *dd=(u8*)d; const u8 *ss=(const u8*)s; while(n--)*dd++=*ss++; return d; }
#endif /* DESHAB_DESKTOP_APP_H */
+25
View File
@@ -0,0 +1,25 @@
CC := clang
LD := ld.lld
ROOT := ../../..
OUT := $(ROOT)/SYSTEM/system/deshab64/tools/editor.elf
CFLAGS := -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.. -I../../UTSM/include -O2
LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld
C_SOURCES := main.c
OBJECTS := $(C_SOURCES:.c=.o)
.PHONY: all clean
all: $(OUT)
$(OUT): $(OBJECTS) linker.ld
@if not exist "$(ROOT)\SYSTEM\system\deshab64\tools" mkdir "$(ROOT)\SYSTEM\system\deshab64\tools"
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }"
+16
View File
@@ -0,0 +1,16 @@
ENTRY(dsk_entry)
PHDRS
{
text PT_LOAD FLAGS(7);
rodata PT_LOAD FLAGS(4);
data PT_LOAD FLAGS(7);
}
SECTIONS
{
. = 0x1000;
.text : ALIGN(4K) { *(.text*) } :text
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
.data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data
}
+238
View File
@@ -0,0 +1,238 @@
/* Deshab Editor — 全屏文本编辑器
* PS/2 键盘输入,行号 + 光标 + 垂直滚动。
* Ctrl+O 打开文件,Ctrl+S 保存(暂不支持),Esc 退出。
*/
#include "../../firstInit/ascii_bitmaps.c"
#include "../desktop_app.h"
#define CHAR_STEP 12
#define CHAR_H 18
#define EDITOR_BUF_SIZE 65536
static da_app_context g_ac;
static char text_buf[EDITOR_BUF_SIZE];
static int text_len = 0;
static int cursor_pos = 0;
static int scroll_y = 0;
static int cursor_line = 0;
static int cursor_col = 0;
static int modified = 0;
static char filename[64] = "untitled";
static int ctrl_down = 0;
static int line_start(int line) {
int l = 0;
for (int i = 0; i < text_len; i++) {
if (l == line) return i;
if (text_buf[i] == '\n') l++;
}
return text_len;
}
static void recalc_cursor(void) {
cursor_line = 0; cursor_col = 0;
for (int i = 0; i < cursor_pos && i < text_len; i++) {
if (text_buf[i] == '\n') { cursor_line++; cursor_col = 0; }
else cursor_col++;
}
}
static void init_default_text(void) {
const char *sample = "# Deshab Editor\n#\n\nStart typing here...\n\nCtrl+O: Open Ctrl+S: Save Esc: Exit\n";
while (*sample && text_len < EDITOR_BUF_SIZE - 1)
text_buf[text_len++] = *sample++;
cursor_pos = text_len;
recalc_cursor();
}
static void redraw_all(void) {
da_fill_bg(&g_ac, DA_BG_SECONDARY);
char title[80];
int p = 0;
const char *t1 = "Editor - ";
while (*t1) title[p++] = *t1++;
for (int i = 0; filename[i] && p < 70; i++) title[p++] = filename[i];
if (modified) { title[p++] = ' '; title[p++] = '*'; }
title[p] = 0;
da_draw_titlebar(&g_ac, title, (i64)g_ac.fb_w);
int margin_x = 8;
int top_y = DA_TITLEBAR_H + 4;
int line_num_w = 5 * CHAR_STEP + 8;
int text_x = margin_x + line_num_w;
int visible_rows = ((int)g_ac.fb_h - top_y - DA_STATUSBAR_H - 8) / CHAR_H;
int visible_cols = ((int)g_ac.fb_w - text_x - 8) / CHAR_STEP;
/* 自动滚动 */
if (cursor_line < scroll_y) scroll_y = cursor_line;
if (cursor_line >= scroll_y + visible_rows) scroll_y = cursor_line - visible_rows + 1;
/* 渲染可见行 */
int line = 0, pos = 0;
for (; pos <= text_len && line < scroll_y + visible_rows; ) {
if (line >= scroll_y) {
int screen_y = top_y + (line - scroll_y) * CHAR_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;
da_draw_string(&g_ac, ln, margin_x, screen_y, DA_TEXT_DIM, DA_BG_SECONDARY, CHAR_STEP);
/* 分隔线 */
da_fill_rect(&g_ac, margin_x + line_num_w - 2, screen_y, 1, CHAR_H, DA_BORDER);
/* 文本 */
int col = 0;
while (pos < text_len && text_buf[pos] != '\n' && col < visible_cols) {
da_draw_char(&g_ac, text_buf[pos],
text_x + col * CHAR_STEP, screen_y,
DA_TEXT_PRIMARY, DA_BG_SECONDARY);
col++; pos++;
}
} else {
while (pos < text_len && text_buf[pos] != '\n') pos++;
}
if (pos < text_len && text_buf[pos] == '\n') pos++;
line++;
if (pos >= text_len) {
/* 空行也画光标 */
if (line > cursor_line) break;
}
}
/* 光标 */
if (cursor_line >= scroll_y && cursor_line < scroll_y + visible_rows) {
int cy = top_y + (cursor_line - scroll_y) * CHAR_H;
int cx = text_x + cursor_col * CHAR_STEP;
da_fill_rect(&g_ac, cx, cy, 2, CHAR_H, DA_ACCENT_LIGHT);
}
/* 状态栏 */
char status[64]; p = 0;
const char *s1 = "L:"; while (*s1) status[p++] = *s1++;
status[p++] = '0' + (cursor_line+1)/10%10; status[p++] = '0' + (cursor_line+1)%10;
const char *s2 = " C:"; while (*s2) status[p++] = *s2++;
status[p++] = '0' + (cursor_col+1)/10%10; status[p++] = '0' + (cursor_col+1)%10;
if (modified) { const char *m = " *"; while (*m) status[p++] = *m++; }
status[p] = 0;
da_draw_statusbar(&g_ac, status, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
}
__attribute__((visibility("default")))
void dsk_entry(const da_boot_context *ctx) {
__asm__ volatile("cli");
da_slog("editor", "boot");
if (!ctx || ctx->magic != DA_BOOT_MAGIC) {
da_slog("editor", "bad context");
for(;;) __asm__("hlt");
}
da_init(&g_ac, ctx);
init_default_text();
redraw_all();
int shift = 0, e0 = 0;
for (;;) {
u8 st = inb(0x64);
if (!(st & 1)) { __asm__("pause"); continue; }
u8 data = inb(0x60);
if (st & 0x20) continue;
u8 sc = data;
if (sc == 0xE0) { e0 = 1; continue; }
if (sc == 0x2A || sc == 0x36) { shift = 1; continue; }
if (sc == 0xAA || sc == 0xB6) { shift = 0; continue; }
if (sc == 0x1D) { ctrl_down = 1; continue; }
if (sc == 0x9D) { ctrl_down = 0; continue; }
if (sc & 0x80) { e0 = 0; continue; }
/* Esc 退出 */
if (sc == 0x01 && !e0) { da_slog("editor", "exit"); return; }
if (e0) {
if (sc == 0x48) { /* Up */
if (cursor_line > 0) {
int prev = line_start(cursor_line - 1);
cursor_pos = prev;
for (int i = 0; i < cursor_col && cursor_pos < text_len && text_buf[cursor_pos] != '\n'; i++)
cursor_pos++;
}
} else if (sc == 0x50) { /* Down */
int next = line_start(cursor_line + 1);
cursor_pos = next;
for (int i = 0; i < cursor_col && cursor_pos < text_len && text_buf[cursor_pos] != '\n'; i++)
cursor_pos++;
} else if (sc == 0x4B) { /* Left */
if (cursor_pos > 0) cursor_pos--;
} else if (sc == 0x4D) { /* Right */
if (cursor_pos < text_len) cursor_pos++;
} else if (sc == 0x47) { /* Home */
cursor_pos = line_start(cursor_line);
} else if (sc == 0x4F) { /* End */
cursor_pos = line_start(cursor_line);
while (cursor_pos < text_len && text_buf[cursor_pos] != '\n') cursor_pos++;
} else if (sc == 0x49) { /* PageUp */
for (int i = 0; i < 10 && cursor_line > 0; i++) {
cursor_pos = line_start(cursor_line - 1);
recalc_cursor();
}
} else if (sc == 0x51) { /* PageDown */
for (int i = 0; i < 10; i++) {
int next = line_start(cursor_line + 1);
if (next >= text_len) break;
cursor_pos = next;
recalc_cursor();
}
}
e0 = 0;
recalc_cursor();
redraw_all();
continue;
}
if (sc == 0x1C) { /* Enter */
if (text_len < EDITOR_BUF_SIZE - 1) {
for (int i = text_len; i > cursor_pos; i--) text_buf[i] = text_buf[i-1];
text_buf[cursor_pos] = '\n';
text_len++; cursor_pos++;
modified = 1;
}
} else if (sc == 0x0E) { /* Backspace */
if (cursor_pos > 0) {
for (int i = cursor_pos - 1; i < text_len - 1; i++) text_buf[i] = text_buf[i+1];
text_len--; cursor_pos--;
modified = 1;
}
} else if (sc == 0x53 && e0) { /* Delete */
if (cursor_pos < text_len) {
for (int i = cursor_pos; i < text_len - 1; i++) text_buf[i] = text_buf[i+1];
text_len--;
modified = 1;
}
} else {
/* Ctrl+O: 打开文件(提示暂不支持) */
if (ctrl_down && (sc == 0x18)) {
/* Ctrl+O - stub */
da_slog("editor", "Ctrl+O - open not yet");
continue;
}
/* Ctrl+S: 保存(提示暂不支持) */
if (ctrl_down && (sc == 0x1F)) {
da_slog("editor", "Ctrl+S - save not yet");
modified = 0;
continue;
}
char c = da_scan_to_ascii(sc, shift);
if (c && c >= 32 && c <= 126 && text_len < EDITOR_BUF_SIZE - 1) {
for (int i = text_len; i > cursor_pos; i--) text_buf[i] = text_buf[i-1];
text_buf[cursor_pos] = c;
text_len++; cursor_pos++;
modified = 1;
}
}
recalc_cursor();
redraw_all();
}
}
+25
View File
@@ -0,0 +1,25 @@
CC := clang
LD := ld.lld
ROOT := ../../..
OUT := $(ROOT)/SYSTEM/system/deshab64/tools/fileman.elf
CFLAGS := -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.. -I../../UTSM/include -O2
LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld
C_SOURCES := main.c
OBJECTS := $(C_SOURCES:.c=.o)
.PHONY: all clean
all: $(OUT)
$(OUT): $(OBJECTS) linker.ld
@if not exist "$(ROOT)\SYSTEM\system\deshab64\tools" mkdir "$(ROOT)\SYSTEM\system\deshab64\tools"
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }"
+16
View File
@@ -0,0 +1,16 @@
ENTRY(dsk_entry)
PHDRS
{
text PT_LOAD FLAGS(7);
rodata PT_LOAD FLAGS(4);
data PT_LOAD FLAGS(7);
}
SECTIONS
{
. = 0x1000;
.text : ALIGN(4K) { *(.text*) } :text
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
.data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data
}
+230
View File
@@ -0,0 +1,230 @@
/* Deshab File Manager — 全屏文件管理器
* 文件列表视图,鼠标点击选择,键盘导航。
* FAT32 根目录遍历,Esc 退出。
*/
#include "../../firstInit/ascii_bitmaps.c"
#include "../desktop_app.h"
#define CHAR_STEP 12
#define CHAR_H 18
#define MAX_FILES 256
static da_app_context g_ac;
static da_cursor g_cursor;
static da_mouse g_mouse;
typedef struct {
char name[13]; /* 8.3 */
int is_dir;
u32 size;
} file_entry;
static file_entry files[MAX_FILES];
static int file_count = 0;
static int selected = 0;
static int scroll_off = 0;
static void load_root_dir(void) {
file_count = 0;
if (!g_ac.block_read) return;
u8 bpb[512];
if (g_ac.block_read(0, 0, 1, bpb) != 0) return;
if (bpb[510] != 0x55 || bpb[511] != 0xAA) return;
u32 root_cluster = *(u32 *)(bpb + 44);
u8 sectors_per_cluster = bpb[13];
u32 fat_start = *(u16 *)(bpb + 14);
u32 data_start = fat_start + (*(u16 *)(bpb + 22)) * 2;
u8 dir_buf[8192];
u32 lba = data_start + (root_cluster - 2) * sectors_per_cluster;
if (g_ac.block_read(0, lba, sectors_per_cluster, dir_buf) != 0) return;
for (int i = 0; i < (int)(sectors_per_cluster * 512) && file_count < MAX_FILES; i += 32) {
u8 first = dir_buf[i];
if (first == 0x00) break;
if (first == 0xE5) continue;
if (dir_buf[i+11] & 0x08) continue;
if (dir_buf[i+11] & 0x10) continue; /* 暂跳过目录 */
file_entry *f = &files[file_count];
f->is_dir = (dir_buf[i+11] & 0x10) ? 1 : 0;
f->size = *(u32 *)(dir_buf + i + 28);
int p = 0;
for (int j = 0; j < 8 && dir_buf[i+j] != ' '; j++)
f->name[p++] = (char)dir_buf[i+j];
if (dir_buf[i+8] != ' ') {
f->name[p++] = '.';
for (int j = 8; j < 11 && dir_buf[i+j] != ' '; j++)
f->name[p++] = (char)dir_buf[i+j];
}
f->name[p] = 0;
file_count++;
}
}
static void redraw_all(void) {
da_fill_bg(&g_ac, DA_BG_PRIMARY);
da_draw_titlebar(&g_ac, "Files", (i64)g_ac.fb_w);
int margin = 12;
int top_y = DA_TITLEBAR_H + 4;
int row_h = CHAR_H + 6;
int visible_rows = ((int)g_ac.fb_h - top_y - DA_STATUSBAR_H - 8) / row_h;
int col_name_x = margin + 32;
int col_size_x = (int)g_ac.fb_w - 120;
int col_type_x = (int)g_ac.fb_w - 60;
/* 列标题 */
da_draw_string(&g_ac, "Name", col_name_x, top_y, DA_TEXT_DIM, DA_BG_PRIMARY, CHAR_STEP);
da_draw_string(&g_ac, "Size", col_size_x, top_y, DA_TEXT_DIM, DA_BG_PRIMARY, CHAR_STEP);
da_draw_string(&g_ac, "Type", col_type_x, top_y, DA_TEXT_DIM, DA_BG_PRIMARY, CHAR_STEP);
da_fill_rect(&g_ac, margin, top_y + CHAR_H, (i64)g_ac.fb_w - margin * 2, 1, DA_BORDER);
/* 文件列表 */
if (scroll_off < 0) scroll_off = 0;
if (scroll_off > file_count - visible_rows && file_count > visible_rows) scroll_off = file_count - visible_rows;
if (scroll_off < 0) scroll_off = 0;
for (int i = 0; i < visible_rows; i++) {
int idx = scroll_off + i;
if (idx >= file_count) break;
int y = top_y + CHAR_H + 6 + i * row_h;
/* 选中高亮 */
if (idx == selected) {
da_fill_rect(&g_ac, margin, y - 2, (i64)g_ac.fb_w - margin * 2, row_h, DA_BG_TERTIARY);
da_rect_outline(&g_ac, margin, y - 2, (i64)g_ac.fb_w - margin * 2, row_h, DA_ACCENT, 4);
}
/* 图标 */
u32 icon_color = files[idx].is_dir ? DA_WARNING : DA_ACCENT;
da_fill_rounded_rect(&g_ac, margin + 4, y, 24, 16, icon_color, 3);
/* 文件名 */
u32 name_color = (idx == selected) ? DA_ACCENT_LIGHT : DA_TEXT_PRIMARY;
da_draw_string(&g_ac, files[idx].name, col_name_x, y, name_color,
(idx == selected) ? DA_BG_TERTIARY : DA_BG_PRIMARY, CHAR_STEP);
/* 大小 */
if (!files[idx].is_dir) {
char sz[16]; int p = 0;
u32 s = files[idx].size;
if (s == 0) { sz[p++] = '0'; }
else { char tmp[12]; int tl = 0; while(s>0){tmp[tl++]='0'+(s%10);s/=10;} for(int j=tl-1;j>=0;j--)sz[p++]=tmp[j]; }
sz[p++] = ' '; sz[p++] = 'B'; sz[p] = 0;
da_draw_string(&g_ac, sz, col_size_x, y, DA_TEXT_DIM,
(idx == selected) ? DA_BG_TERTIARY : DA_BG_PRIMARY, CHAR_STEP);
} else {
da_draw_string(&g_ac, "--", col_size_x, y, DA_TEXT_DIM,
(idx == selected) ? DA_BG_TERTIARY : DA_BG_PRIMARY, CHAR_STEP);
}
/* 类型 */
const char *type = files[idx].is_dir ? "DIR" : "FILE";
da_draw_string(&g_ac, type, col_type_x, y, DA_TEXT_DIM,
(idx == selected) ? DA_BG_TERTIARY : DA_BG_PRIMARY, CHAR_STEP);
}
if (file_count == 0) {
da_draw_string(&g_ac, "No files found (empty or no block device)",
margin + 20, top_y + 60, DA_TEXT_DIM, DA_BG_PRIMARY, CHAR_STEP);
}
/* 状态栏 */
char status[64]; int p = 0;
const char *p1 = "/ "; while (*p1) status[p++] = *p1++;
status[p++] = '0' + file_count / 10; status[p++] = '0' + file_count % 10;
const char *p2 = " items"; while (*p2) status[p++] = *p2++;
status[p] = 0;
da_draw_statusbar(&g_ac, status, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
/* 鼠标光标 */
da_cursor_save(&g_ac, &g_cursor);
da_cursor_draw(&g_ac, &g_cursor, DA_CURSOR_COLOR);
}
__attribute__((visibility("default")))
void dsk_entry(const da_boot_context *ctx) {
__asm__ volatile("cli");
da_slog("fileman", "boot");
if (!ctx || ctx->magic != DA_BOOT_MAGIC) {
da_slog("fileman", "bad context");
for(;;) __asm__("hlt");
}
da_init(&g_ac, ctx);
da_cursor_init(&g_cursor, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
da_mouse_init();
load_root_dir();
redraw_all();
int e0 = 0, shift = 0; (void)shift;
for (;;) {
int need_redraw = 0;
/* 鼠标轮询 */
if (da_mouse_poll(&g_mouse, &g_cursor, (i64)g_ac.fb_w, (i64)g_ac.fb_h)) {
need_redraw = 1;
if (g_cursor.btn) {
/* 点击选择文件 */
int margin = 12;
int top_y = DA_TITLEBAR_H + 4;
int row_h = CHAR_H + 6;
int click_y = g_cursor.my - top_y - CHAR_H - 6;
if (click_y >= 0) {
int idx = click_y / row_h + scroll_off;
if (idx >= 0 && idx < file_count) {
selected = idx;
need_redraw = 1;
}
}
}
}
/* 键盘轮询 */
u8 st = inb(0x64);
if ((st & 1) && !(st & 0x20)) {
u8 data = inb(0x60);
u8 sc = data;
if (sc == 0xE0) { e0 = 1; goto skip; }
if (sc == 0x2A || sc == 0x36) { shift = 1; goto skip; }
if (sc == 0xAA || sc == 0xB6) { shift = 0; goto skip; }
if (sc & 0x80) { e0 = 0; goto skip; }
if (sc == 0x01) { da_slog("fileman", "exit"); return; }
if (e0) {
if (sc == 0x48) { /* Up */
if (selected > 0) selected--;
need_redraw = 1;
} else if (sc == 0x50) { /* Down */
if (selected < file_count - 1) selected++;
need_redraw = 1;
}
e0 = 0;
goto skip;
}
if (sc == 0x1C) { /* Enter - 打开 */
if (selected >= 0 && selected < file_count) {
da_slog("fileman", "open file (stub)");
}
need_redraw = 1;
} else if (sc == 0x0E) { /* Backspace - 返回上级 */
da_slog("fileman", "back (root only)");
need_redraw = 1;
}
}
skip:
if (need_redraw) {
da_cursor_restore(&g_ac, &g_cursor);
redraw_all();
}
__asm__("pause");
}
}
+25
View File
@@ -0,0 +1,25 @@
CC := clang
LD := ld.lld
ROOT := ../../..
OUT := $(ROOT)/SYSTEM/system/deshab64/tools/ping.elf
CFLAGS := -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.. -I../../UTSM/include -O2
LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld
C_SOURCES := main.c
OBJECTS := $(C_SOURCES:.c=.o)
.PHONY: all clean
all: $(OUT)
$(OUT): $(OBJECTS) linker.ld
@if not exist "$(ROOT)\SYSTEM\system\deshab64\tools" mkdir "$(ROOT)\SYSTEM\system\deshab64\tools"
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }"
+16
View File
@@ -0,0 +1,16 @@
ENTRY(dsk_entry)
PHDRS
{
text PT_LOAD FLAGS(7);
rodata PT_LOAD FLAGS(4);
data PT_LOAD FLAGS(7);
}
SECTIONS
{
. = 0x1000;
.text : ALIGN(4K) { *(.text*) } :text
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
.data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data
}
+339
View File
@@ -0,0 +1,339 @@
/* Deshab ping — 终端风格 ICMP Echo 工具 stub
* 命令行输入 IP 地址,发送 ICMP Echo Request。
* 当前为 stub,提示需要 TCP/IP 栈支持。
* Esc 键退出返回 DSK。
*/
#include "../../firstInit/ascii_bitmaps.c"
#include "../desktop_app.h"
/* ---- 终端参数 ---- */
#define TERM_COLS 120
#define TERM_ROWS 40
#define TERM_MAX_CHARS (TERM_COLS * TERM_ROWS)
#define CHAR_STEP 12
#define CHAR_H 18
#define PROMPT_STR "ping> "
static da_app_context g_ac;
/* 终端缓冲区 */
static u8 term_ch[TERM_MAX_CHARS];
static u32 term_fg[TERM_MAX_CHARS];
static int term_w = TERM_COLS;
static int term_h = TERM_ROWS;
static int cur_col = 0;
static int cur_row = 0;
/* 输入缓冲 */
static char input_buf[256];
static int input_len = 0;
static int input_cursor = 0;
/* 命令历史 */
#define HIST_SIZE 16
static char history[HIST_SIZE][256];
static int hist_count = 0;
static int hist_idx = 0;
/* 坐标 */
static int term_x, term_y;
static void term_init_layout(void) {
term_x = 8;
term_y = DA_TITLEBAR_H + 4;
int avail_w = (int)g_ac.fb_w - 16;
int avail_h = (int)g_ac.fb_h - term_y - DA_STATUSBAR_H - 8;
term_w = avail_w / CHAR_STEP;
term_h = avail_h / CHAR_H;
if (term_w < 10) term_w = 10;
if (term_h < 5) term_h = 5;
if (term_w > TERM_COLS) term_w = TERM_COLS;
if (term_h > TERM_ROWS) term_h = TERM_ROWS;
}
static void term_clear(void) {
for (int i = 0; i < TERM_MAX_CHARS; i++) {
term_ch[i] = ' ';
term_fg[i] = DA_TEXT_PRIMARY;
}
cur_col = 0; cur_row = 0;
}
static void term_scroll(void) {
for (int r = 1; r < term_h; r++) {
for (int c = 0; c < term_w; c++) {
term_ch[(r-1)*TERM_COLS+c] = term_ch[r*TERM_COLS+c];
term_fg[(r-1)*TERM_COLS+c] = term_fg[r*TERM_COLS+c];
}
}
for (int c = 0; c < term_w; c++) {
term_ch[(term_h-1)*TERM_COLS+c] = ' ';
term_fg[(term_h-1)*TERM_COLS+c] = DA_TEXT_PRIMARY;
}
cur_row = term_h - 1; cur_col = 0;
}
static void term_newline(void) {
cur_col = 0; cur_row++;
if (cur_row >= term_h) term_scroll();
}
static void term_putc_color(char c, u32 color) {
if (c == '\n') { term_newline(); return; }
if (c == '\r') { cur_col = 0; return; }
if (c == '\t') { int sp = 4 - (cur_col % 4); for (int i = 0; i < sp; i++) term_putc_color(' ', color); return; }
if (c < ' ' || c > '~') c = '?';
if (cur_col >= term_w) term_newline();
int idx = cur_row * TERM_COLS + cur_col;
term_ch[idx] = (u8)c; term_fg[idx] = color;
cur_col++;
}
static void term_putc(char c) { term_putc_color(c, DA_TEXT_PRIMARY); }
static void term_puts_color(const char *s, u32 color) { while (*s) term_putc_color(*s++, color); }
static void term_puts(const char *s) { term_puts_color(s, DA_TEXT_PRIMARY); }
/* ---- 绘制 ---- */
static void redraw_all(void) {
da_fill_bg(&g_ac, DA_BG_PRIMARY);
da_draw_titlebar(&g_ac, "Ping", (i64)g_ac.fb_w);
/* 终端字符 */
for (int r = 0; r < term_h; r++) {
for (int c = 0; c < term_w; c++) {
int idx = r * TERM_COLS + c;
if (term_ch[idx] == ' ') continue;
da_draw_char(&g_ac, term_ch[idx],
term_x + c * CHAR_STEP, term_y + r * CHAR_H,
term_fg[idx], DA_BG_PRIMARY);
}
}
/* 光标 */
if (cur_col < term_w && cur_row < term_h) {
da_fill_rect(&g_ac, term_x + cur_col * CHAR_STEP,
term_y + cur_row * CHAR_H + CHAR_H - 3, 11, 2, DA_ACCENT);
}
/* 状态栏 */
char status[64];
int p = 0;
const char *s1 = "ICMP Echo | Esc 退出 ";
while (*s1) status[p++] = *s1++;
u8 hh, mm; da_rtc_time(&hh, &mm);
status[p++] = '0' + hh/10; status[p++] = '0' + hh%10;
status[p++] = ':'; status[p++] = '0' + mm/10; status[p++] = '0' + mm%10;
status[p] = 0;
da_draw_statusbar(&g_ac, status, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
}
/* ---- IP 格式校验 ---- */
static int is_valid_ip(const char *s) {
int dots = 0, digits = 0;
while (*s) {
if (*s == '.') { dots++; digits = 0; }
else if (*s >= '0' && *s <= '9') { digits++; if (digits > 3) return 0; }
else return 0;
s++;
}
return dots == 3;
}
/* ---- Ping 模拟 ---- */
static void do_ping(const char *ip) {
term_puts_color("PING ", DA_PROMPT_COLOR);
term_puts_color(ip, DA_ACCENT_LIGHT);
term_puts_color(" 56(84) bytes of data.\n", DA_TEXT_DIM);
/* stub: 检查 kernel_api 是否有 net 接口 */
if (!g_ac.kernel_api) {
term_puts_color(" [错误] 无内核 API\n", DA_ERROR);
return;
}
u64 net_api = *(u64 *)((u64)g_ac.kernel_api + 0xC0);
if (!net_api) {
term_puts_color(" [错误] 需要 TCP/IP 栈支持\n", DA_ERROR);
term_puts_color(" 当前内核未实现 ICMP 协议栈。\n", DA_WARNING);
term_puts_color(" 请等待后续版本更新。\n", DA_TEXT_DIM);
term_putc('\n');
term_puts_color("--- ", DA_TEXT_DIM);
term_puts_color(ip, DA_TEXT_DIM);
term_puts_color(" ping statistics ---\n", DA_TEXT_DIM);
term_puts_color(" 0 packets transmitted, 0 received, 100% packet loss\n", DA_ERROR);
return;
}
/* 如果有 net API,此处应实现真正的 ICMP Echo */
term_puts_color(" [stub] 网络功能尚未实现\n", DA_WARNING);
}
/* ---- 内置命令 ---- */
static int str_eq(const char *a, const char *b) {
while (*a && *b) { if (*a != *b) return 0; a++; b++; }
return *a == *b;
}
static void cmd_help(void) {
term_puts_color("ping — ICMP Echo 请求工具\n", DA_PROMPT_COLOR);
term_puts(" <IP> 发送 ICMP Echo Request\n");
term_puts(" help 显示此帮助\n");
term_puts(" clear 清屏\n");
term_puts(" version 显示版本\n");
term_puts(" Esc 退出\n");
}
static void cmd_clear(void) { term_clear(); }
static void cmd_version(void) {
term_puts_color("Deshab ping v0.1 (stub)\n", DA_SUCCESS);
term_puts_color(" ICMP Echo 功能需要 TCP/IP 栈支持\n", DA_TEXT_DIM);
}
static void execute_command(const char *cmd) {
while (*cmd == ' ') cmd++;
if (*cmd == 0) return;
if (str_eq(cmd, "help") || str_eq(cmd, "?")) { cmd_help(); return; }
if (str_eq(cmd, "clear") || str_eq(cmd, "cls")) { cmd_clear(); return; }
if (str_eq(cmd, "version") || str_eq(cmd, "ver")) { cmd_version(); return; }
/* 非内置命令视为 IP 地址 */
if (is_valid_ip(cmd)) {
do_ping(cmd);
} else {
term_puts_color("无效 IP 地址: ", DA_ERROR);
term_puts_color(cmd, DA_ERROR);
term_putc('\n');
term_puts_color("请输入有效的 IPv4 地址 (如 192.168.1.1)\n", DA_TEXT_DIM);
}
}
static void add_history(const char *cmd) {
if (hist_count < HIST_SIZE) {
int i;
for (i = 0; cmd[i] && i < 255; i++) history[hist_count][i] = cmd[i];
history[hist_count][i] = 0;
hist_count++;
}
hist_idx = hist_count;
}
/* ---- 输入行重绘 ---- */
static void redraw_input_line(void) {
int plen = 0;
while (PROMPT_STR[plen]) plen++;
i64 y = term_y + (i64)cur_row * CHAR_H;
da_fill_rect(&g_ac, term_x, y, (i64)term_w * CHAR_STEP, CHAR_H, DA_BG_PRIMARY);
for (int i = 0; i < plen; i++)
da_draw_char(&g_ac, PROMPT_STR[i], term_x + i * CHAR_STEP, y, DA_PROMPT_COLOR, DA_BG_PRIMARY);
for (int i = 0; i < input_len; i++)
da_draw_char(&g_ac, input_buf[i], term_x + (plen + i) * CHAR_STEP, y, DA_TEXT_PRIMARY, DA_BG_PRIMARY);
cur_col = plen + input_cursor;
da_fill_rect(&g_ac, term_x + cur_col * CHAR_STEP, y + CHAR_H - 3, 11, 2, DA_ACCENT);
}
/* ---- 主入口 ---- */
__attribute__((visibility("default")))
void dsk_entry(const da_boot_context *ctx) {
__asm__ volatile("cli");
da_slog("ping", "boot");
if (!ctx || ctx->magic != DA_BOOT_MAGIC) {
da_slog("ping", "bad context");
for(;;) __asm__("hlt");
}
da_init(&g_ac, ctx);
term_init_layout();
term_clear();
/* 启动 banner */
term_puts_color("=== Deshab ping v0.1 (stub) ===\n", DA_ACCENT);
term_puts_color("ICMP Echo 请求工具\n", DA_SUCCESS);
term_puts_color("输入 IP 地址发送 ping | 输入 'help' 查看帮助 | Esc 退出\n", DA_TEXT_DIM);
term_puts_color("注意: 当前需要 TCP/IP 栈支持\n", DA_WARNING);
term_putc('\n');
redraw_all();
int shift = 0, e0 = 0;
for (;;) {
term_puts_color(PROMPT_STR, DA_PROMPT_COLOR);
input_len = 0; input_cursor = 0; input_buf[0] = 0;
int plen = 0; while (PROMPT_STR[plen]) plen++;
cur_col = plen;
redraw_all();
int cmd_done = 0;
while (!cmd_done) {
u8 st = inb(0x64);
if (!(st & 1)) { __asm__("pause"); continue; }
u8 data = inb(0x60);
if (st & 0x20) continue;
u8 sc = data;
if (sc == 0xE0) { e0 = 1; continue; }
if (sc == 0x2A || sc == 0x36) { shift = 1; continue; }
if (sc == 0xAA || sc == 0xB6) { shift = 0; continue; }
if (sc & 0x80) { e0 = 0; continue; }
/* Esc 退出 */
if (sc == 0x01 && !e0) {
da_slog("ping", "exit");
return;
}
if (e0) {
if (sc == 0x4B) { if (input_cursor > 0) input_cursor--; }
else if (sc == 0x4D) { if (input_cursor < input_len) input_cursor++; }
else if (sc == 0x47) { input_cursor = 0; }
else if (sc == 0x4F) { input_cursor = input_len; }
else if (sc == 0x48) {
if (hist_idx > 0) {
hist_idx--;
int i; for (i = 0; history[hist_idx][i] && i < 255; i++) input_buf[i] = history[hist_idx][i];
input_len = i; input_cursor = i; input_buf[i] = 0;
}
}
else if (sc == 0x50) {
if (hist_idx < hist_count - 1) {
hist_idx++;
int i; for (i = 0; history[hist_idx][i] && i < 255; i++) input_buf[i] = history[hist_idx][i];
input_len = i; input_cursor = i; input_buf[i] = 0;
} else if (hist_idx < hist_count) {
hist_idx = hist_count;
input_len = 0; input_cursor = 0; input_buf[0] = 0;
}
}
e0 = 0;
redraw_input_line();
continue;
}
if (sc == 0x1C) { /* Enter */
input_buf[input_len] = 0;
term_putc('\n');
if (input_len > 0) add_history(input_buf);
execute_command(input_buf);
cmd_done = 1;
continue;
}
if (sc == 0x0E) { /* Backspace */
if (input_cursor > 0) {
for (int i = input_cursor - 1; i < input_len - 1; i++)
input_buf[i] = input_buf[i+1];
input_len--; input_cursor--;
input_buf[input_len] = 0;
}
redraw_input_line();
continue;
}
char c = da_scan_to_ascii(sc, shift);
if (c && c >= 32 && c <= 126 && input_len < 255) {
for (int i = input_len; i > input_cursor; i--)
input_buf[i] = input_buf[i-1];
input_buf[input_cursor++] = c;
input_len++;
redraw_input_line();
}
}
redraw_all();
}
}
+461
View File
@@ -0,0 +1,461 @@
# 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
+528
View File
@@ -0,0 +1,528 @@
# Deshab 工具应用设计文档
## 1. 概述
Deshab 工具应用是独立开发、独立编译的 .elf 可执行文件,运行在桌面管理器(Desktop Manager)环境中。
每个工具在 `CODE/tools/{name}/` 下拥有独立的 `main.c` + `MAKEFILE` + `linker.ld`,
通过 DSK 的 FAT32 + ELF loader 加载,接收 `dsk_boot_context` 系统上下文。
**核心约束**:
- 所有应用运行在 Ring0,单地址空间,无内存隔离
- 独立工具通过 `dsk_entry(dsk_boot_context *ctx)` 入口点启动
- 工具可以直接访问 framebuffer、PS/2 输入、kernel_api
- 网络工具依赖 kernel_api.net,文件工具依赖 FAT32 block_read
- 公共辅助代码通过 `CODE/tools/desktop_app.h` 共享
**开发模式演进**:
- **当前阶段**:3 个内置应用(Bash/Editor/Calculator)使用回调模式编译在 desktop.elf 内
- **目标阶段**:工具应用独立开发为单独 .elf,桌面管理器通过 ELF 加载方式载入
## 2. 应用 ABI
### 2.1 独立工具入口
独立工具 .elf 实现以下入口点:
```c
__attribute__((visibility("default")))
void dsk_entry(const dsk_boot_context *ctx);
```
工具接收完整的 `dsk_boot_context`,包含:
```c
typedef struct dsk_boot_context {
u64 magic; /* 0x44534B31424F4F54 ("DSK1BOOT") */
u32 abi_version; /* 1 */
u32 size;
u64 flags;
u64 hhdm_offset;
u64 rsdp_address;
u64 framebuffer_address; /* 帧缓冲物理地址 (+ HHDM) */
u64 framebuffer_width;
u64 framebuffer_height;
u64 framebuffer_pitch;
u32 framebuffer_bpp;
u64 boot_modules_response;
u64 dkm_kernel_api; /* DKM kernel_api 分发表 */
u64 dkm_driver_table;
u64 dkm_driver_count;
u64 utsm_state;
u64 drr_state;
u64 memory_map;
u64 memory_map_count;
u64 memory_map_entry_size;
u64 kernel_stack_top;
} dsk_boot_context;
```
### 2.2 公共辅助头文件
`CODE/tools/desktop_app.h` 提供所有工具共享的定义和辅助函数:
```c
/* 类型定义 */
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef long long i64;
/* COM1 串口日志 */
#define COM1 0x3F8
static inline void outb(u16 port, u8 value);
static inline u8 inb(u16 port);
void sputc(char c);
void swrite(const char *s);
void slog(const char *s);
/* PS/2 键盘轮询 */
int ps2kbd_poll(u8 *scancode); /* 返回 1=有数据 */
/* PS/2 鼠标轮询 */
int ps2mouse_poll(int *dx, int *dy, int *btn); /* 返回 1=有数据包 */
/* scan code set 1 → ASCII 映射 */
char scan_to_ascii(u8 sc, int shift);
/* 帧缓冲绘制辅助 */
void fb_init(u64 addr, u64 w, u64 h, u64 pitch);
void fb_pixel(int x, int y, u32 color);
void fb_rect(int x, int y, int w, int h, u32 color);
void fb_string(const char *s, int x, int y, u32 fg, u32 bg, int step);
/* FAT32 文件读取辅助 */
typedef int (*block_read_fn)(u32 index, u64 lba, u32 count, void *buf);
int fat32_read_file(block_read_fn br, const char *path, u8 **data, u32 *size);
/* 窗口概念(标题栏+关闭按钮+客户区) */
#define WIN_TITLEBAR_H 28
#define WIN_BORDER_W 2
void win_draw_frame(int x, int y, int w, int h, const char *title, int focused);
int win_hit_close(int wx, int wy, int ww, int wh, int mx, int my);
```
### 2.3 当前内置应用 ABI(回调模式)
当前 3 个内置应用编译在 desktop.elf 内,使用回调模式:
```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);
```
### 2.4 应用生命周期
**回调式(当前)**:
```
1. 用户点击图标/快速启动按钮
2. 桌面管理器创建窗口
3. 桌面调用 app->on_create(ctx) → 返回 app_state
4. 事件循环:桌面调用 app->on_event(app_state, ctx, ev)
5. 绘制:桌面调用 app->on_draw(app_state, ctx)
6. 用户关闭窗口 → 桌面调用 app->on_destroy(app_state)
7. 桌面销毁窗口,回收资源
```
**独立 ELF 式(未来)**:
```
1. 桌面管理器通过 FAT32 读取工具 .elf
2. PIE ELF loader 加载 + 重定位
3. 调用 dsk_entry(ctx)
4. 工具自行接管输入/渲染/主循环
5. 工具返回 → 桌面管理器恢复控制
```
## 3. 工具应用详细设计
### 3.1 终端 (Bash) — CODE/tools/bash/
**状态**:内置版本已实现(回调模式);独立版本待开发
**功能**:
- 接收 dsk_boot_context,全屏暗色终端
- PS/2 键盘输入,命令历史(↑↓键)
- 内置命令:help, clear, echo, version/ver, uname, date, about, reboot, halt, pwd, whoami, id, ls, cat
- Sealed Arc 暗色主题(DS_DARK_* 色系)
**UI 设计**:
```
┌─ deshab# ────────────────────────────[✕]─┐
│ deshab# echo "Hello, Deshab!" │
│ Hello, Deshab! │
│ deshab# _ │
│ │
│ │
│ │
├──────────────────────────────────────────┤
│ / 12:34 │
└──────────────────────────────────────────┘
```
**独立版本结构**:
- 标题栏:`deshab#` 标题 + 右侧红色 X 关闭按钮
- 主区域:暗色终端输出区 + 当前输入行 + 闪烁光标
- 状态栏(底部 20px):左侧当前目录 `/`,右侧 RTC 时间
**数据结构**:
```c
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; /* 提示符长度 */
char history[16][256]; /* 命令历史 */
int history_count; /* 历史条数 */
int history_pos; /* 历史浏览位置 */
} bash_state;
```
**命令实现**:
| 命令 | 功能 | 依赖 |
|------|------|------|
| help | 显示可用命令列表 | 无 |
| clear | 清屏 | 无 |
| echo | 回显参数 | 无 |
| version / ver | 显示版本 "Deshab OS v0.1.0" | 无 |
| uname | 显示系统名 "Deshab" | 无 |
| date | 显示 RTC 日期时间 | CMOS |
| about | 显示系统信息 | 无 |
| reboot | 重启(0x64→0xFE) | 键盘控制器 |
| halt | 停机(HLT) | 无 |
| pwd | 显示当前目录 "/" | 无 |
| whoami | 显示 "root" | 无 |
| id | 显示 "uid=0(root) gid=0(root)" | 无 |
| ls | 列出目录内容 | FAT32 增强遍历 |
| cat | 显示文件内容 | FAT32 读取 |
---
### 3.2 文本编辑器 (Editor) — CODE/tools/editor/
**状态**:内置版本已实现(回调模式);独立版本待开发
**功能**:
- 全屏编辑器,行号 + 编辑区 + 状态栏
- FAT32 文件打开(通过 kernel_api block_read)
- 编辑操作:插入/删除字符、换行、光标移动(方向键/Home/End)
- PageUp/PageDown 翻页
- 文本选择:Shift+方向键
- Ctrl+S 保存(需 FAT32 write API)
- 状态栏:行:列、文件名、修改标记
**UI 设计**:
```
┌─ Editor — README.TXT ───────────────[✕]─┐
│ 1│ # Deshab OS │
│ 2│ 单地址空间 Ring0 内核实验系统 │
│ 3│ │
│ 4│ ## 概述 │
│ 5│ Deshab 是一个...█ │
│ │ │ │
│ │ │ │
├───┴──────────────────────────────────────┤
│ L:5 C:15 README.TXT * │
└──────────────────────────────────────────┘
```
**数据结构**:
```c
typedef struct {
char text[EDITOR_BUF_SIZE]; /* 文本缓冲 (64KB) */
int text_len; /* 文本长度 */
int cursor_pos; /* 光标位置(字节偏移) */
int scroll_y; /* 垂直滚动偏移(行数) */
int cursor_line; /* 光标行号 */
int cursor_col; /* 光标列号 */
int sel_start, sel_end; /* 选择范围 */
int modified; /* 修改标记 */
char filename[128]; /* 当前文件名 */
} editor_state;
```
**渲染细节**:
- 行号:3 位数字宽度(`DS_DARK_TEXT_DIM` 颜色)+ 1px 分隔线(`DS_DARK_DIVIDER`)
- 编辑区背景:`DS_DARK_BG_SECONDARY`
- 光标:2px 宽竖线,`DS_DARK_CURSOR` 颜色
- 状态栏:底部 20px,`DP_ABYSS_800` 背景,显示 `L:行 C:列 文件名 *修改标记`
---
### 3.3 文件管理器 (FileManager) — CODE/tools/fileman/
**状态**:占位(当前 desktop.elf 中 fileman 无回调);独立版本待开发
**功能**:
- 双栏布局:左侧目录树、右侧文件列表
- FAT32 目录遍历(需增强到子目录)
- 文件图标区分:目录/ELF/TXT/CONF
- 双击打开:.txt/.conf → 编辑器,.elf → 执行
- 路径栏 + 返回按钮
**UI 设计**:
```
┌─ 文件管理器 ────────────────────────[✕]─┐
│ [←] /system/deshab64/ │
├──────────┬───────────────────────────────┤
│ / │ 📁 tools/ DIR │
│ /system │ 📄 desktop.elf 53KB ELF │
│ /driver │ 📄 editor.elf 28KB ELF │
│ │ 📄 deshab.elf 131KB ELF │
│ │ 📄 README.TXT 2KB TXT │
│ │ 📄 user.conf 1KB CONF │
├──────────┴───────────────────────────────┤
│ 6 项 │
└──────────────────────────────────────────┘
```
**文件图标颜色**:
| 类型 | 图标 | 主色调 |
|------|------|--------|
| 目录 | 📁 | DP_ARC_500 (紫) |
| ELF | 📄 + 可执行标记 | DP_SEAL_300 (青) |
| TXT | 📄 + 文本标记 | DP_NEUTRAL_300 (灰) |
| CONF | 📄 + 齿轮标记 | DP_WARNING (琥珀) |
**依赖**:FAT32 子目录遍历(当前 DSK 仅支持根目录读取,需增强)
---
### 3.4 浏览器 (Browser) — CODE/tools/browser/
**状态**:Phase 4 实现(依赖 TCP/IP + HTTP + HTML 渲染)
**功能**:
- 地址栏 + 导航按钮(后退/前进/刷新)
- HTTP GET 请求(通过 kernel_api.net)
- 简单 HTML 渲染:标题、段落、链接、列表
- 超链接点击导航
- 历史记录(后退/前进栈)
**UI 设计**:
```
┌─ 浏览器 ────────────────────────────[✕]─┐
│ [←][→][↻] [http://example.com ][Go] │
├──────────────────────────────────────────┤
│ Example Domain │
│ │
│ This domain is for use in illustrative │
│ examples in documents. You may use this │
│ domain in literature... │
│ │
│ More information... │
└──────────────────────────────────────────┘
```
**依赖**:
- TCP/IP 栈(需实现 TCP 连接、发送/接收、拥塞控制)
- HTTP/1.1 客户端(GET 请求、响应解析)
- DNS 解析(netman 已实现)
- HTML 解析器(简化版,仅处理常见标签)
---
### 3.5 HTTP 客户端 (curl) — CODE/tools/curl/
**状态**:Phase 3 实现(依赖 TCP/IP 最小实现)
**功能**:
- 命令行界面运行(终端风格)
- HTTP GET/HEAD 请求
- URL 解析(host:port/path)
- 响应头显示(`-i` 选项)
- 超时处理
**命令行**:
```
curl http://example.com
curl -i http://example.com
curl --head http://example.com
```
**UI 设计**:
```
┌─ curl ──────────────────────────────[✕]─┐
│ > curl http://example.com │
│ HTTP/1.1 200 OK │
│ Content-Type: text/html │
│ │
│ <!doctype html> │
│ <html> │
│ ... │
└──────────────────────────────────────────┘
```
**依赖**:TCP/IP 栈 + DNS 解析
**实现方式**:作为窗口化终端风格应用运行,输出 HTTP 响应内容。
---
### 3.6 ICMP 工具 (ping) — CODE/tools/ping/
**状态**:Phase 3 实现(依赖 ICMP + ARP)
**功能**:
- 发送 ICMP Echo Request
- 接收 ICMP Echo Reply
- RTT 计算(基于 PIT 已校准计时器)
- 统计信息显示(发送/接收/丢包率/平均 RTT)
**命令行**:
```
ping 192.168.1.1
ping -c 4 10.0.2.2
```
**UI 设计**:
```
┌─ ping ──────────────────────────────[✕]─┐
│ PING 192.168.1.1 │
│ 64 bytes: seq=0 time=2ms │
│ 64 bytes: seq=1 time=1ms │
│ 64 bytes: seq=2 time=3ms │
│ │
│ --- 192.168.1.1 ping statistics --- │
│ 3 packets transmitted, 3 received, 0% │
│ RTT min/avg/max = 1/2/3 ms │
└──────────────────────────────────────────┘
```
**依赖**:
- ICMP 协议实现(需通过 kernel_api.net 发送原始 IP 包)
- ARP 解析(netman 已实现)
- 计时器(PIT 已校准)
---
### 3.7 更多应用(规划中)
| 应用 | 描述 | Phase | 依赖 |
|------|------|-------|------|
| System Monitor | CPU/内存/网络状态 | 2 | kernel_api |
| Paint | 简单绘图工具 | 2 | 无 |
| Settings | 系统偏好设置 | 2 | FAT32 write |
| Screenshot | 屏幕截图保存 | 3 | FAT32 write |
| Music Player | PC 喇叭/AC97 音频 | 4 | 音频驱动 |
## 4. 依赖矩阵
```
FAT32 Read | FAT32 Write | TCP/IP | ICMP | DNS | ARP | PS/2输入
bash ✓ | | | | | | ✓
editor ✓ | (保存) | | | | | ✓
fileman ✓ | | | | | | ✓
browser ✓ | | ✓ | | ✓ | ✓ | ✓
curl ✓ | | ✓ | | ✓ | ✓ | ✓
ping ✓ | | | ✓ | | ✓ | ✓
calculator | | | | | | | ✓
```
## 5. 应用图标设计
每个应用使用 32×32 像素图标,程序化生成(C 函数绘制,无需位图文件)。
| 应用 | 图标描述 | 主色调 | 绘制函数 |
|------|----------|--------|----------|
| bash | 终端窗口 >_ | DP_SEAL_300 (亮青) | `draw_icon_terminal()` |
| editor | 笔记本+文本行 | DP_SEAL_500 (青) | `draw_icon_editor()` |
| fileman | 文件夹 | DP_ARC_500 (紫) | `draw_icon_folder()` |
| calculator | 计算器按键 | DP_WARNING (琥珀) | `draw_icon_calc()` |
| browser | 地球/指南针 | DP_SEAL_300 (亮青) | 待实现 |
| curl | 箭头+网络 | DP_SEAL_700 (深青) | 待实现 |
| ping | 信号波 | DP_SUCCESS (绿) | 待实现 |
**图标绘制规范**:
- 圆角矩形背景(6px 圆角,`DU_RADIUS_SM`)
- 1px 描边(使用边框色)
- 前景图形使用应用主色调
- 背景色:DP_ABYSS_700
## 6. 构建集成
### 6.1 独立工具 MAKEFILE
每个工具有独立的 MAKEFILE(与 desktop.elf 相同模式):
```makefile
# tools/bash/MAKEFILE 示例
CC := clang
LD := ld.lld
ROOT := ../../..
OUT := $(ROOT)/SYSTEM/system/deshab64/tools/bash.elf
CFLAGS := -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.. -I../../UTSM/include -O2
LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld
C_SOURCES := main.c
OBJECTS := $(C_SOURCES:.c=.o)
.PHONY: all clean
all: $(OUT)
$(OUT): $(OBJECTS) linker.ld
@if not exist "$(ROOT)\SYSTEM\system\deshab64\tools" mkdir "$(ROOT)\SYSTEM\system\deshab64\tools"
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
```
### 6.2 build.ps1 集成
在 `build.ps1` 中添加工具构建步骤:
```powershell
# 构建工具应用
$tools = @("bash", "editor", "fileman", "curl", "ping", "browser")
foreach ($tool in $tools) {
Push-Location "CODE/tools/$tool"
& make -f MAKEFILE
Pop-Location
}
```
### 6.3 FAT32 镜像更新
`mkfat32.c` 中添加工具 .elf 入口:
```
SYSTEM/system/deshab64/tools/bash.elf
SYSTEM/system/deshab64/tools/editor.elf
SYSTEM/system/deshab64/tools/fileman.elf
SYSTEM/system/deshab64/tools/curl.elf
SYSTEM/system/deshab64/tools/ping.elf
SYSTEM/system/deshab64/tools/browser.elf
```
## 7. 测试策略
- **单元测试**:每个应用的事件处理回调可用模拟事件测试
- **QEMU 集成测试**:完整的启动→桌面→打开应用→操作→关闭流程
- **串口日志**:所有应用通过 COM1 输出调试信息(`[bash]`、`[editor]` 等前缀)
- **FAT32 验证**:使用 `driver/test.fat32` 测试镜像验证文件读取功能
- **独立工具验证**:DSK 直接加载工具 .elf,检查 dsk_entry 入口调用和上下文传递
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+45 -11
View File
@@ -567,6 +567,46 @@ if (-not (Test-Path $ShellOutput)) {
} }
Write-Host "[build] shell Output: $ShellOutput" Write-Host "[build] shell Output: $ShellOutput"
$DesktopDir = Join-Path $Root 'CODE\desktop'
$DesktopOutput = Join-Path $SystemDir 'system\deshab64\desktop.elf'
Write-Host '[build] Building desktop.elf...'
try {
& $make -C "$DesktopDir" -f MAKEFILE "CC=$clang" "LD=$lld"
if ($LASTEXITCODE -ne 0) {
throw "make desktop failed with exit code $LASTEXITCODE"
}
}
finally {
}
if (-not (Test-Path $DesktopOutput)) {
throw "desktop build did not produce $DesktopOutput"
}
Write-Host "[build] desktop Output: $DesktopOutput"
# ---------- Tool applications ----------
$ToolsDir = Join-Path $Root 'CODE\tools'
$ToolsOutDir = Join-Path $SystemDir 'system\deshab64\tools'
$toolApps = @('bash', 'editor', 'fileman', 'browser', 'curl', 'ping')
foreach ($tool in $toolApps) {
$toolDir = Join-Path $ToolsDir $tool
$toolMake = Join-Path $toolDir 'MAKEFILE'
if (Test-Path $toolMake) {
Write-Host "[build] Building ${tool}.elf..."
try {
& $make -C "$toolDir" -f MAKEFILE "CC=$clang" "LD=$lld"
if ($LASTEXITCODE -ne 0) {
Write-Host "[build] WARNING: make $tool failed with exit code $LASTEXITCODE"
}
}
finally {
}
}
}
Write-Host '[build] Building DKM network drivers...' Write-Host '[build] Building DKM network drivers...'
Build-DkmDriver (Join-Path $Root 'CODE\DKM\e1000\e1000.c') (Join-Path $SystemDir 'driver\net\e1000.drv') Build-DkmDriver (Join-Path $Root 'CODE\DKM\e1000\e1000.c') (Join-Path $SystemDir 'driver\net\e1000.drv')
Build-DkmDriver (Join-Path $Root 'CODE\DKM\virtio_net\virtio_net.c') (Join-Path $SystemDir 'driver\net\virtio_net.drv') Build-DkmDriver (Join-Path $Root 'CODE\DKM\virtio_net\virtio_net.c') (Join-Path $SystemDir 'driver\net\virtio_net.drv')
@@ -581,19 +621,13 @@ Write-Host "[build] Done: $ImagePath"
# Rebuild SATA FAT32 disk image (DSK reads deshab.elf/FirstInit.elf/mouseInit.elf from here) # Rebuild SATA FAT32 disk image (DSK reads deshab.elf/FirstInit.elf/mouseInit.elf from here)
Write-Host '[build] Rebuilding SATA FAT32 disk image...' Write-Host '[build] Rebuilding SATA FAT32 disk image...'
$BuildTmp = Join-Path $Root '.build_tmp' $BuildTmp = Join-Path $Root '.build_tmp'
$MkFat32 = Join-Path $BuildTmp 'mkfat32.exe' $MkFat32Ps = Join-Path $BuildTmp 'mkfat32.ps1'
if (Test-Path $MkFat32) { if (Test-Path $MkFat32Ps) {
Push-Location $BuildTmp & powershell -ExecutionPolicy Bypass -File $MkFat32Ps
try {
& $MkFat32
if ($LASTEXITCODE -ne 0) { if ($LASTEXITCODE -ne 0) {
Write-Host "[build] WARNING: mkfat32 exited with $LASTEXITCODE" Write-Host "[build] WARNING: mkfat32.ps1 exited with $LASTEXITCODE"
}
}
finally {
Pop-Location
} }
Write-Host "[build] SATA IMG: $(Join-Path $BuildTmp 'sata_fat32_dsk.img')" Write-Host "[build] SATA IMG: $(Join-Path $BuildTmp 'sata_fat32_dsk.img')"
} else { } else {
Write-Host '[build] WARNING: mkfat32.exe not found, SATA image not rebuilt' Write-Host '[build] WARNING: mkfat32.ps1 not found, SATA image not rebuilt'
} }