diff --git a/.gitignore b/.gitignore index c48c072..5fe8947 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,6 @@ ISO/deshab.img +*.o +.build_tmp/ +.trae/ +docs/workbuddy-planning/ +sata_fat32_dsk.img diff --git a/CODE/UTSM/core/process.c b/CODE/UTSM/core/process.c index 941ddd3..d4aa0a2 100644 --- a/CODE/UTSM/core/process.c +++ b/CODE/UTSM/core/process.c @@ -1,11 +1,126 @@ +/* process.c — UTSM 进程上下文管理 + * + * 进程表分配/释放、capability table 操作、crypto context 管理。 + */ + #include #include +#include +#include + +/* ---- 进程表 ---- */ +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) { process->process_uuid = uuid_make_test(seed); process->crypto_epoch = 1; process->recovery_generation = drr_recovery_generation(); - process->capability_table_slot = 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; } diff --git a/CODE/UTSM/drr/drr_stub.c b/CODE/UTSM/drr/drr_stub.c index f1860d1..60b721b 100644 --- a/CODE/UTSM/drr/drr_stub.c +++ b/CODE/UTSM/drr/drr_stub.c @@ -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 #include -static const u64 g_root_key[4] = { - 0x4452525554534d31ULL, - 0xfeedfacecafebeefULL, - 0x0123456789abcdefULL, - 0xfedcba9876543210ULL +/* ---- 内部内存操作(不依赖 libc) ---- */ +static void drr_memzero(void *dst, u64 n) { + u8 *d = (u8 *)dst; + while (n--) *d++ = 0; +} + +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) { - return g_root_key; + return g_drr.root_key; } u64 drr_recovery_generation(void) { - return 1; + return g_drr.recovery_generation; } +/* ---- Fault 报告 ---- */ void drr_report_fault(const char *reason) { - log_error("[DRR] fault reported"); + if (!g_drr.initialized) return; + log_error("[DRR] FAULT: "); 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) { - UTSM_UNUSED(segment_slot); - UTSM_UNUSED(offset); - UTSM_UNUSED(len); + if (!g_drr.initialized || !g_drr.log_base) return; + if (g_drr.log_count >= g_drr.log_capacity) { + /* 日志满,最简单的策略:丢弃最早的条目 */ + 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) { - UTSM_UNUSED(segment_slot); - UTSM_UNUSED(offset); - UTSM_UNUSED(len); + if (!g_drr.initialized || !g_drr.log_base) return; + if (g_drr.log_count >= g_drr.log_capacity) { + 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; } diff --git a/CODE/UTSM/include/utsm/deshab_ui.h b/CODE/UTSM/include/utsm/deshab_ui.h new file mode 100644 index 0000000..ba60b97 --- /dev/null +++ b/CODE/UTSM/include/utsm/deshab_ui.h @@ -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 */ diff --git a/CODE/UTSM/include/utsm/drr.h b/CODE/UTSM/include/utsm/drr.h index 9bff1f3..808b692 100644 --- a/CODE/UTSM/include/utsm/drr.h +++ b/CODE/UTSM/include/utsm/drr.h @@ -2,12 +2,126 @@ #define UTSM_DRR_H #include +#include -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); u64 drr_recovery_generation(void); void drr_report_fault(const char *reason); void drr_log_write_intent(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 */ diff --git a/CODE/UTSM/include/utsm/process.h b/CODE/UTSM/include/utsm/process.h index 2b1243d..5b01267 100644 --- a/CODE/UTSM/include/utsm/process.h +++ b/CODE/UTSM/include/utsm/process.h @@ -3,16 +3,77 @@ #include #include +#include +#include +/* ---- 进程标志 ---- */ +#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 { uuid128_t process_uuid; u64 crypto_epoch; u64 recovery_generation; - u32 capability_table_slot; + u32 process_slot; /* 进程表索引 */ u32 capability_count; 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; -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 */ diff --git a/CODE/UTSM/kernel/main.c b/CODE/UTSM/kernel/main.c index ed1d63a..53fb922 100644 --- a/CODE/UTSM/kernel/main.c +++ b/CODE/UTSM/kernel/main.c @@ -28,9 +28,9 @@ void kernel_main(void) { net_init(); log_info("[UTSM] net registry init ok"); - log_info("[UTSM] drr stub init begin"); - drr_stub_init(); - log_info("[UTSM] drr stub init ok"); + log_info("[UTSM] drr init begin"); + drr_init(); + log_info("[UTSM] drr init ok"); log_info("[UTSM] core init begin"); utsm_init(); diff --git a/CODE/desktop/MAKEFILE b/CODE/desktop/MAKEFILE new file mode 100644 index 0000000..3d6f2f9 --- /dev/null +++ b/CODE/desktop/MAKEFILE @@ -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 }" diff --git a/CODE/desktop/linker.ld b/CODE/desktop/linker.ld new file mode 100644 index 0000000..94dddf7 --- /dev/null +++ b/CODE/desktop/linker.ld @@ -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 +} diff --git a/CODE/desktop/main.c b/CODE/desktop/main.c new file mode 100644 index 0000000..d938fd2 --- /dev/null +++ b/CODE/desktop/main.c @@ -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"); + } +} diff --git a/CODE/dsk/main.c b/CODE/dsk/main.c index 22d94c7..9eac4bb 100644 --- a/CODE/dsk/main.c +++ b/CODE/dsk/main.c @@ -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 */ void(*entry)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))fe_entry; entry(ctx); - logl("[DSK] FirstInit returned, loading shell"); - /* FirstInit 返回后加载 shell */ - char sh[12]; /* SHELL 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'; - u8 *sd=0; u32 ss=0; void *se=0; - if(fat32_read_root_file(sh,&sd,&ss)==0){ - if(dsk_load_elf(sd,ss,&se)==0){ - logl("[DSK] jumping to shell"); - void(*sentry)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))se; - sentry(ctx); + logl("[DSK] FirstInit returned, loading desktop"); + /* FirstInit 返回后加载 desktop */ + char dn[12]; /* DESKTOP ELF */ + 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 *dd=0; u32 ds=0; void *de=0; + if(fat32_read_root_file(dn,&dd,&ds)==0){ + if(dsk_load_elf(dd,ds,&de)==0){ + logl("[DSK] jumping to desktop"); + void(*dentry)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))de; + dentry(ctx); } } for(;;)__asm__("hlt"); } - /* 非首次启动:直接加载 shell */ + /* 非首次启动:直接加载 desktop */ if(checked&&!fe_entry&&frame>150){ - logl("[DSK] loading shell directly (non-first boot)"); - char sh[12]; /* SHELL 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'; - u8 *sd=0; u32 ss=0; void *se=0; - if(fat32_read_root_file(sh,&sd,&ss)==0){ - if(dsk_load_elf(sd,ss,&se)==0){ - logl("[DSK] jumping to shell"); - void(*sentry)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))se; - sentry(ctx); + logl("[DSK] loading desktop directly (non-first boot)"); + char dn[12]; /* DESKTOP ELF */ + 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 *dd=0; u32 ds=0; void *de=0; + if(fat32_read_root_file(dn,&dd,&ds)==0){ + if(dsk_load_elf(dd,ds,&de)==0){ + logl("[DSK] jumping to desktop"); + void(*dentry)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))de; + dentry(ctx); } } for(;;)__asm__("hlt"); diff --git a/CODE/shell/main.c b/CODE/shell/main.c index 9c8c4c8..ed8a9da 100644 --- a/CODE/shell/main.c +++ b/CODE/shell/main.c @@ -1,11 +1,10 @@ /* Deshab Shell — 命令行交互界面 * 接收 PS/2 键盘输入,执行内置命令。 - * 显示在 framebuffer 上,使用 Consolas 18px 等宽字体。 + * 使用 Deshab "Sealed Arc" 视觉风格系统。 */ #include "../UTSM/include/utsm/dsk.h" -typedef signed char i8; typedef unsigned char u8; typedef unsigned short u16; 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 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 "../UTSM/include/utsm/deshab_ui.h" -#define ASCII_W 11 -#define ASCII_H 18 -#define ASCII_STEP 11 +/* ---- 风格令牌(引用 deshab_ui.h 语义色) ---- */ +#define BG_COLOR DS_DARK_BG_PRIMARY +#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_ROWS 35 #define TERM_MAX_CHARS (TERM_COLS * TERM_ROWS) -/* 颜色 — 深色主题,类似经典终端 */ -#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 du_context g_ctx; static u64 fb_a, fb_w, fb_h, fb_p; -static u32 *g_fb; /* 终端缓冲区 — 字符 + 颜色 */ static u8 term_ch[TERM_MAX_CHARS]; @@ -56,61 +64,18 @@ static int cur_col = 0; static int cur_row = 0; static int cur_visible = 1; -/* 颜色混合 */ -static u32 blend(u32 c1, u32 c2, u32 a) { - 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= fb_h) continue; - u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p); - for (i64 c=0; c= 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= fb_h) continue; - u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p); - for (i64 c=0; c= fb_w) continue; - line[(u64)xx] = color; - } - } -} - -/* ---- 终端逻辑 ---- */ -static int term_start_x, term_start_y; /* 像素坐标 */ +/* ---- 终端像素坐标 ---- */ +static int term_start_x, term_start_y; static void term_init_pos(void) { - /* 居中布局,每字符 11px 宽,行高 20px */ - int margin_x = 16; - int margin_y = 16; + int margin_x = (int)DU_SPACE_MD; 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_h = (int)fb_h - margin_y * 2; - term_w = avail_w / ASCII_STEP; - term_h = avail_h / (ASCII_H + 2); + int avail_h = (int)fb_h - term_start_y - STATUSBAR_H - (int)DU_SPACE_SM; + term_w = avail_w / (int)DU_ASCII_STEP; + term_h = avail_h / (int)DU_ASCII_LINE_H; if (term_w < 10) term_w = 10; if (term_h < 5) term_h = 5; if (term_w > TERM_COLS) term_w = TERM_COLS; @@ -127,7 +92,6 @@ static void term_clear(void) { } static void term_scroll(void) { - /* 向上滚动一行 */ for (int r = 1; r < term_h; r++) { for (int c = 0; c < term_w; c++) { int src = r * TERM_COLS + c; @@ -136,7 +100,6 @@ static void term_scroll(void) { term_fg[dst] = term_fg[src]; } } - /* 清空最后一行 */ for (int c = 0; c < term_w; c++) { term_ch[(term_h-1) * TERM_COLS + c] = ' '; 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_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) { /* 清屏背景 */ - 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 c = 0; c < term_w; c++) { int idx = r * TERM_COLS + c; u8 ch = term_ch[idx]; if (ch == ' ') continue; - i64 x = term_start_x + (i64)c * ASCII_STEP; - i64 y = term_start_y + (i64)r * (ASCII_H + 2); - fb_char(g_fb, ch, x, y, term_fg[idx], BG_COLOR); + i64 x = term_start_x + (i64)c * (i64)DU_ASCII_STEP; + i64 y = term_start_y + (i64)r * (i64)DU_ASCII_LINE_H; + du_draw_char(&g_ctx, ch, x, y, term_fg[idx], BG_COLOR); } } - /* 渲染光标 */ + /* 光标 */ if (cur_visible && cur_col < term_w && cur_row < term_h) { - i64 x = term_start_x + (i64)cur_col * ASCII_STEP; - i64 y = term_start_y + (i64)cur_row * (ASCII_H + 2); - fill_rect(g_fb, x, y + ASCII_H - 3, ASCII_W, 2, TEXT_FG); + i64 x = term_start_x + (i64)cur_col * (i64)DU_ASCII_STEP; + i64 y = term_start_y + (i64)cur_row * (i64)DU_ASCII_LINE_H; + 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) { if (cur_visible && cur_col < term_w && cur_row < term_h) { - i64 x = term_start_x + (i64)cur_col * ASCII_STEP; - i64 y = term_start_y + (i64)cur_row * (ASCII_H + 2); - fill_rect(g_fb, x, y + ASCII_H - 3, ASCII_W, 2, TEXT_FG); + i64 x = term_start_x + (i64)cur_col * (i64)DU_ASCII_STEP; + i64 y = term_start_y + (i64)cur_row * (i64)DU_ASCII_LINE_H; + 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) { if (cur_col < term_w && cur_row < term_h) { - i64 x = term_start_x + (i64)cur_col * ASCII_STEP; - i64 y = term_start_y + (i64)cur_row * (ASCII_H + 2); - fill_rect(g_fb, x, y + ASCII_H - 3, ASCII_W, 2, BG_COLOR); + i64 x = term_start_x + (i64)cur_col * (i64)DU_ASCII_STEP; + i64 y = term_start_y + (i64)cur_row * (i64)DU_ASCII_LINE_H; + 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 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 const char normal[58] = { 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(" ABI: DKM (Deshab Kernel Module) v1\n"); term_puts(" Bootloader: Limine\n"); + term_puts_color(" Style: Sealed Arc (Deshab UI)\n", DIM_FG); } static void cmd_uname(void) { @@ -274,20 +277,20 @@ static void cmd_about(void) { term_puts_color("=== Deshab OS ===\n", PROMPT_FG); term_puts("单地址空间 Ring0 内核实验系统\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("© 2026 Deshab Project\n", DIM_FG); + term_puts_color("(c) 2026 Deshab Project\n", DIM_FG); } static void cmd_clear(void) { term_clear(); + term_redraw_all(); } static void cmd_reboot(void) { term_puts_color("Rebooting...\n", PROMPT_FG); term_redraw_all(); - /* ACPI 重启:写 0x64 = 0xFE 触发键盘控制器复位 */ outb(0x64, 0xFE); - /* 备用:三重故障 */ __asm__ volatile("int $0x03"); for(;;) __asm__("hlt"); } @@ -303,22 +306,18 @@ static void cmd_echo(const char *args) { term_putc('\n'); } -/* 前向声明 — CMOS RTC 读取 */ static u8 read_rtc_reg(u8 reg); static u8 bcd_to_bin_impl(u8 bcd); static void cmd_date(void) { - /* 通过 CMOS RTC 读取日期 */ u8 year = bcd_to_bin_impl(read_rtc_reg(9)); u8 month = bcd_to_bin_impl(read_rtc_reg(8)); u8 day = bcd_to_bin_impl(read_rtc_reg(7)); u8 hour = bcd_to_bin_impl(read_rtc_reg(4)); u8 minute = bcd_to_bin_impl(read_rtc_reg(2)); u8 second = bcd_to_bin_impl(read_rtc_reg(0)); - /* 简单打印 */ char buf[32]; int p = 0; - /* YYYY-MM-DD HH:MM:SS */ buf[p++] = '2'; buf[p++] = '0'; buf[p++] = '0' + year / 10; buf[p++] = '0' + year % 10; buf[p++] = '-'; @@ -349,21 +348,16 @@ static void cmd_not_impl(const char *cmd) { } static void execute_command(const char *cmd) { - /* 跳过前导空格 */ while (*cmd == ' ') cmd++; if (*cmd == 0) return; - /* 找命令名(第一个单词) */ const char *args = cmd; while (*args && *args != ' ') args++; int cmd_name_len = (int)(args - cmd); - /* 复制命令名到本地缓冲 */ char name[32]; if (cmd_name_len >= 32) cmd_name_len = 31; for (int i = 0; i < cmd_name_len; i++) name[i] = cmd[i]; name[cmd_name_len] = 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(); @@ -402,30 +396,26 @@ static void draw_prompt(void) { /* ---- 输入行编辑 ---- */ static void redraw_input_line(void) { - /* 清除当前行从提示符开始的部分并重绘 */ int prompt_len = 0; while (PROMPT[prompt_len]) prompt_len++; - /* 清除整行 */ - i64 y = term_start_y + (i64)cur_row * (ASCII_H + 2); - fill_rect(g_fb, term_start_x, y, (i64)term_w * ASCII_STEP, ASCII_H, BG_COLOR); - /* 重绘提示符 */ + i64 y = term_start_y + (i64)cur_row * (i64)DU_ASCII_LINE_H; + du_fill_rect(&g_ctx, term_start_x, y, + (i64)term_w * (i64)DU_ASCII_STEP, + (i64)DU_ASCII_CELL_H, BG_COLOR); for (int i = 0; i < prompt_len; i++) { - i64 x = term_start_x + (i64)i * ASCII_STEP; - fb_char(g_fb, PROMPT[i], x, y, PROMPT_FG, BG_COLOR); + i64 x = term_start_x + (i64)i * (i64)DU_ASCII_STEP; + du_draw_char(&g_ctx, PROMPT[i], x, y, PROMPT_FG, BG_COLOR); } - /* 重绘输入缓冲 */ for (int i = 0; i < input_len; i++) { - i64 x = term_start_x + (i64)(prompt_len + i) * ASCII_STEP; - fb_char(g_fb, input_buf[i], x, y, TEXT_FG, BG_COLOR); + i64 x = term_start_x + (i64)(prompt_len + i) * (i64)DU_ASCII_STEP; + du_draw_char(&g_ctx, input_buf[i], x, y, TEXT_FG, BG_COLOR); } - /* 同步 cur_col 到输入光标 */ cur_col = prompt_len + input_cursor; term_redraw_cursor(); } static void insert_char(char c) { if (input_len >= 255) return; - /* 在 input_cursor 处插入 */ for (int i = input_len; i > input_cursor; i--) { input_buf[i] = input_buf[i-1]; } @@ -437,7 +427,6 @@ static void insert_char(char c) { static void delete_char_back(void) { if (input_cursor == 0) return; - /* 删除 input_cursor-1 处的字符 */ for (int i = input_cursor - 1; i < input_len - 1; i++) { 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_h = ctx->framebuffer_height; 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_clear(); term_redraw_all(); - /* 启动 banner */ - term_puts_color("=== Deshab OS v0.1.0 ===\n", PROMPT_FG); + /* 启动 banner — 使用风格令牌 */ + term_puts_color("=== Deshab OS v0.1.0 ===\n", ACCENT_FG); term_puts_color("欢迎使用 Deshab Shell\n", OK_FG); term_puts_color("输入 'help' 查看可用命令\n", DIM_FG); term_putc('\n'); @@ -548,47 +539,40 @@ void dsk_entry(const dsk_boot_context *ctx) { cur_col = prompt_len; term_redraw_cursor(); - /* 等待命令输入 */ int cmd_done = 0; while (!cmd_done) { u8 st = inb(0x64); if (!(st & 1)) { __asm__("pause"); continue; } u8 data = inb(0x60); - /* 忽略鼠标数据(AUX 位) */ 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) { - /* 松开事件 — 仅处理扩展键 */ - if (e0 && sc == 0x9C) { e0 = 0; } /* Enter 释放 */ - else if (e0 && sc == 0xCB) { e0 = 0; } /* Left 释放 */ - else if (e0 && sc == 0xCD) { e0 = 0; } /* Right 释放 */ - else if (e0 && sc == 0xC8) { e0 = 0; } /* Up 释放 */ - else if (e0 && sc == 0xD0) { e0 = 0; } /* Down 释放 */ - else if (e0 && sc == 0xD3) { e0 = 0; } /* Delete 释放 */ - else if (e0 && sc == 0xC7) { e0 = 0; } /* Home 释放 */ - else if (e0 && sc == 0xCF) { e0 = 0; } /* End 释放 */ + if (e0 && sc == 0x9C) { e0 = 0; } + else if (e0 && sc == 0xCB) { e0 = 0; } + else if (e0 && sc == 0xCD) { e0 = 0; } + else if (e0 && sc == 0xC8) { e0 = 0; } + else if (e0 && sc == 0xD0) { e0 = 0; } + else if (e0 && sc == 0xD3) { e0 = 0; } + else if (e0 && sc == 0xC7) { e0 = 0; } + else if (e0 && sc == 0xCF) { e0 = 0; } e0 = 0; continue; } - /* 按下事件 */ if (e0) { - /* 扩展键 */ - if (sc == 0x4B) cursor_left(); /* Left */ - else if (sc == 0x4D) cursor_right(); /* Right */ - else if (sc == 0x47) cursor_home(); /* Home */ - else if (sc == 0x4F) cursor_end(); /* End */ - else if (sc == 0x53) delete_char_fwd(); /* Delete */ - else if (sc == 0x48) { /* Up — 命令历史暂未实现 */ } - else if (sc == 0x50) { /* Down — 命令历史暂未实现 */ } + if (sc == 0x4B) cursor_left(); + else if (sc == 0x4D) cursor_right(); + else if (sc == 0x47) cursor_home(); + else if (sc == 0x4F) cursor_end(); + else if (sc == 0x53) delete_char_fwd(); + else if (sc == 0x48) { } + else if (sc == 0x50) { } e0 = 0; continue; } - /* 控制键 */ if (sc == 0x1C) { - /* Enter — 执行命令 */ term_clear_cursor(); input_buf[input_len] = 0; term_putc('\n'); @@ -596,32 +580,11 @@ void dsk_entry(const dsk_boot_context *ctx) { cmd_done = 1; continue; } - if (sc == 0x0E) { - /* Backspace */ - delete_char_back(); - 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; - } - /* 普通字符 */ + if (sc == 0x0E) { delete_char_back(); continue; } + if (sc == 0x01) { kill_line(); continue; } + if (sc == 0x15 && !shift) { kill_line(); continue; } + if (sc == 0x17 && !shift) { while (input_cursor > 0) delete_char_back(); continue; } + if (sc == 0x0B && !shift) { kill_to_end(); continue; } char c = scan_to_ascii(sc, shift); if (c && c >= 32 && c <= 126) { insert_char(c); diff --git a/CODE/tools/bash/MAKEFILE b/CODE/tools/bash/MAKEFILE new file mode 100644 index 0000000..1a9b8a2 --- /dev/null +++ b/CODE/tools/bash/MAKEFILE @@ -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 }" diff --git a/CODE/tools/bash/linker.ld b/CODE/tools/bash/linker.ld new file mode 100644 index 0000000..94dddf7 --- /dev/null +++ b/CODE/tools/bash/linker.ld @@ -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 +} diff --git a/CODE/tools/bash/main.c b/CODE/tools/bash/main.c new file mode 100644 index 0000000..790ee6c --- /dev/null +++ b/CODE/tools/bash/main.c @@ -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 显示文本\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 显示文件内容\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(); + } +} diff --git a/CODE/tools/browser/MAKEFILE b/CODE/tools/browser/MAKEFILE new file mode 100644 index 0000000..7d04ada --- /dev/null +++ b/CODE/tools/browser/MAKEFILE @@ -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 }" diff --git a/CODE/tools/browser/linker.ld b/CODE/tools/browser/linker.ld new file mode 100644 index 0000000..94dddf7 --- /dev/null +++ b/CODE/tools/browser/linker.ld @@ -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 +} diff --git a/CODE/tools/browser/main.c b/CODE/tools/browser/main.c new file mode 100644 index 0000000..26844ad --- /dev/null +++ b/CODE/tools/browser/main.c @@ -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"); + } +} diff --git a/CODE/tools/curl/MAKEFILE b/CODE/tools/curl/MAKEFILE new file mode 100644 index 0000000..d0bef11 --- /dev/null +++ b/CODE/tools/curl/MAKEFILE @@ -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 }" diff --git a/CODE/tools/curl/linker.ld b/CODE/tools/curl/linker.ld new file mode 100644 index 0000000..94dddf7 --- /dev/null +++ b/CODE/tools/curl/linker.ld @@ -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 +} diff --git a/CODE/tools/curl/main.c b/CODE/tools/curl/main.c new file mode 100644 index 0000000..b06724c --- /dev/null +++ b/CODE/tools/curl/main.c @@ -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(" 发送 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(); + } +} diff --git a/CODE/tools/desktop_app.h b/CODE/tools/desktop_app.h new file mode 100644 index 0000000..82cada8 --- /dev/null +++ b/CODE/tools/desktop_app.h @@ -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 */ diff --git a/CODE/tools/editor/MAKEFILE b/CODE/tools/editor/MAKEFILE new file mode 100644 index 0000000..da9a6d9 --- /dev/null +++ b/CODE/tools/editor/MAKEFILE @@ -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 }" diff --git a/CODE/tools/editor/linker.ld b/CODE/tools/editor/linker.ld new file mode 100644 index 0000000..94dddf7 --- /dev/null +++ b/CODE/tools/editor/linker.ld @@ -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 +} diff --git a/CODE/tools/editor/main.c b/CODE/tools/editor/main.c new file mode 100644 index 0000000..fdb863a --- /dev/null +++ b/CODE/tools/editor/main.c @@ -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(); + } +} diff --git a/CODE/tools/fileman/MAKEFILE b/CODE/tools/fileman/MAKEFILE new file mode 100644 index 0000000..bb408c8 --- /dev/null +++ b/CODE/tools/fileman/MAKEFILE @@ -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 }" diff --git a/CODE/tools/fileman/linker.ld b/CODE/tools/fileman/linker.ld new file mode 100644 index 0000000..94dddf7 --- /dev/null +++ b/CODE/tools/fileman/linker.ld @@ -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 +} diff --git a/CODE/tools/fileman/main.c b/CODE/tools/fileman/main.c new file mode 100644 index 0000000..41b69be --- /dev/null +++ b/CODE/tools/fileman/main.c @@ -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"); + } +} diff --git a/CODE/tools/ping/MAKEFILE b/CODE/tools/ping/MAKEFILE new file mode 100644 index 0000000..2eef1fd --- /dev/null +++ b/CODE/tools/ping/MAKEFILE @@ -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 }" diff --git a/CODE/tools/ping/linker.ld b/CODE/tools/ping/linker.ld new file mode 100644 index 0000000..94dddf7 --- /dev/null +++ b/CODE/tools/ping/linker.ld @@ -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 +} diff --git a/CODE/tools/ping/main.c b/CODE/tools/ping/main.c new file mode 100644 index 0000000..f7fc219 --- /dev/null +++ b/CODE/tools/ping/main.c @@ -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(" 发送 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(); + } +} diff --git a/RE/docs/desktop_architecture.md b/RE/docs/desktop_architecture.md new file mode 100644 index 0000000..466cf99 --- /dev/null +++ b/RE/docs/desktop_architecture.md @@ -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 diff --git a/RE/docs/tool_apps_design.md b/RE/docs/tool_apps_design.md new file mode 100644 index 0000000..0019252 --- /dev/null +++ b/RE/docs/tool_apps_design.md @@ -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 │ +│ │ +│ │ +│ │ +│ ... │ +└──────────────────────────────────────────┘ +``` + +**依赖**: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 入口调用和上下文传递 diff --git a/SYSTEM/boot/utsm.elf b/SYSTEM/boot/utsm.elf index 68557ec..49b8d3c 100644 Binary files a/SYSTEM/boot/utsm.elf and b/SYSTEM/boot/utsm.elf differ diff --git a/SYSTEM/system/deshab64/deshab.elf b/SYSTEM/system/deshab64/deshab.elf index 692c218..f69adaf 100644 Binary files a/SYSTEM/system/deshab64/deshab.elf and b/SYSTEM/system/deshab64/deshab.elf differ diff --git a/SYSTEM/system/deshab64/desktop.elf b/SYSTEM/system/deshab64/desktop.elf new file mode 100644 index 0000000..e4ccfa5 Binary files /dev/null and b/SYSTEM/system/deshab64/desktop.elf differ diff --git a/SYSTEM/system/deshab64/shell.elf b/SYSTEM/system/deshab64/shell.elf index de56ea0..4b6d814 100644 Binary files a/SYSTEM/system/deshab64/shell.elf and b/SYSTEM/system/deshab64/shell.elf differ diff --git a/SYSTEM/system/deshab64/tools/bash.elf b/SYSTEM/system/deshab64/tools/bash.elf new file mode 100644 index 0000000..341d4c6 Binary files /dev/null and b/SYSTEM/system/deshab64/tools/bash.elf differ diff --git a/SYSTEM/system/deshab64/tools/browser.elf b/SYSTEM/system/deshab64/tools/browser.elf new file mode 100644 index 0000000..954ee56 Binary files /dev/null and b/SYSTEM/system/deshab64/tools/browser.elf differ diff --git a/SYSTEM/system/deshab64/tools/curl.elf b/SYSTEM/system/deshab64/tools/curl.elf new file mode 100644 index 0000000..ec3da1c Binary files /dev/null and b/SYSTEM/system/deshab64/tools/curl.elf differ diff --git a/SYSTEM/system/deshab64/tools/editor.elf b/SYSTEM/system/deshab64/tools/editor.elf new file mode 100644 index 0000000..2ab35c8 Binary files /dev/null and b/SYSTEM/system/deshab64/tools/editor.elf differ diff --git a/SYSTEM/system/deshab64/tools/fileman.elf b/SYSTEM/system/deshab64/tools/fileman.elf new file mode 100644 index 0000000..ea25f32 Binary files /dev/null and b/SYSTEM/system/deshab64/tools/fileman.elf differ diff --git a/SYSTEM/system/deshab64/tools/ping.elf b/SYSTEM/system/deshab64/tools/ping.elf new file mode 100644 index 0000000..dc6f2f6 Binary files /dev/null and b/SYSTEM/system/deshab64/tools/ping.elf differ diff --git a/build.ps1 b/build.ps1 index 8608bb3..16fe132 100644 --- a/build.ps1 +++ b/build.ps1 @@ -567,6 +567,46 @@ if (-not (Test-Path $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...' 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') @@ -581,19 +621,13 @@ Write-Host "[build] Done: $ImagePath" # Rebuild SATA FAT32 disk image (DSK reads deshab.elf/FirstInit.elf/mouseInit.elf from here) Write-Host '[build] Rebuilding SATA FAT32 disk image...' $BuildTmp = Join-Path $Root '.build_tmp' -$MkFat32 = Join-Path $BuildTmp 'mkfat32.exe' -if (Test-Path $MkFat32) { - Push-Location $BuildTmp - try { - & $MkFat32 - if ($LASTEXITCODE -ne 0) { - Write-Host "[build] WARNING: mkfat32 exited with $LASTEXITCODE" - } - } - finally { - Pop-Location +$MkFat32Ps = Join-Path $BuildTmp 'mkfat32.ps1' +if (Test-Path $MkFat32Ps) { + & powershell -ExecutionPolicy Bypass -File $MkFat32Ps + if ($LASTEXITCODE -ne 0) { + Write-Host "[build] WARNING: mkfat32.ps1 exited with $LASTEXITCODE" } Write-Host "[build] SATA IMG: $(Join-Path $BuildTmp 'sata_fat32_dsk.img')" } 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' }