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

This commit is contained in:
xyou
2026-07-03 21:13:04 +08:00
parent 3662345434
commit 48dcb5568e
44 changed files with 6302 additions and 220 deletions
+117 -2
View File
@@ -1,11 +1,126 @@
/* process.c — UTSM 进程上下文管理
*
* 进程表分配/释放、capability table 操作、crypto context 管理。
*/
#include <utsm/process.h>
#include <utsm/drr.h>
#include <utsm/arena.h>
#include <utsm/log.h>
/* ---- 进程表 ---- */
static utsm_process_context g_process_table[UTSM_MAX_PROCESSES];
static u32 g_process_count;
void utsm_process_init(void) {
log_info("[UTSM] process table init");
for (u32 i = 0; i < UTSM_MAX_PROCESSES; i++) {
g_process_table[i].state = UTSM_PROC_UNUSED;
g_process_table[i].process_slot = i;
g_process_table[i].cap_table.count = 0;
g_process_table[i].cap_table.generation = 0;
}
g_process_count = 0;
}
void utsm_process_create_test(utsm_process_context *process, u64 seed) {
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;
}
+294 -15
View File
@@ -1,37 +1,316 @@
/* drr_core.c — DRR Dedicated Recovery Root 核心实现
*
* 替换原 drr_stub.c,提供完整的:
* - Emergency Pool(独立于普通堆分配器)
* - Recovery Log(write intent/commit 追踪)
* - Checkpoint A/B 双槽(CRC 校验、原子 active slot 切换)
* - Root key 管理
* - Heartbeat
*/
#include <utsm/drr.h>
#include <utsm/log.h>
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;
}
+708
View File
@@ -0,0 +1,708 @@
/* deshab_ui.h — Deshab 视觉风格系统
*
* "Sealed Arc" 设计语言:以 UTSM 封缄内存为灵感的统一视觉体系。
*
* 核心设计隐喻:
* Arc(弧) — 封缄/解封的视觉表达,圆角、弧形边框、弧线动画
* Seal(封) — 密封容器的安全感,卡片容器、发光边缘、透明覆盖
* Flow(流) — 数据流的连续性,渐变背景、平滑过渡、流式布局
*
* 设计令牌分层:
* L0 — 原始色值(Deshab Palette)
* L1 — 语义色值(Semantic Colors,引用 L0)
* L2 — 组件令牌(Component Tokens,引用 L1)
*
* 所有 UI 组件必须通过本头文件获取视觉参数,禁止硬编码颜色/尺寸。
*/
#ifndef DESHAB_UI_H
#define DESHAB_UI_H
/* ===================================================================
* 基础类型(自包含,不依赖 utsm/types.h 以便 DSK/Shell 独立使用)
* =================================================================== */
typedef unsigned char du_u8;
typedef unsigned short du_u16;
typedef unsigned int du_u32;
typedef unsigned long long du_u64;
typedef long long du_i64;
/* ===================================================================
* L0 — Deshab 原始色板 (Deshab Palette)
*
* 色彩命名规则:{色调}_{明度}
* 格式:0xAARRGGBB(与 framebuffer 一致,小端 x86 上 BB 置低位)
* =================================================================== */
/* --- 主色调:深海蓝 (Abyss Blue) --- */
#define DP_ABYSS_900 0xFF0A1428u /* 最深:终端背景、底色 */
#define DP_ABYSS_800 0xFF0F1E38u /* 深层:暗卡片背景 */
#define DP_ABYSS_700 0xFF162848u /* 暗面:悬停态背景 */
#define DP_ABYSS_600 0xFF1E3868u /* 中暗:输入框背景 */
#define DP_ABYSS_500 0xFF284888u /* 中间:次级强调 */
#define DP_ABYSS_400 0xFF3868B0u /* 中亮:默认强调 */
#define DP_ABYSS_300 0xFF5088D0u /* 亮面:链接色 */
#define DP_ABYSS_200 0xFF78A8E8u /* 高亮:焦点指示 */
#define DP_ABYSS_100 0xFFA8CCF4u /* 最亮:浅色辅助 */
/* --- 强调色:封缄青 (Seal Cyan) — 主要操作/交互 --- */
#define DP_SEAL_900 0xFF004858u
#define DP_SEAL_700 0xFF007898u
#define DP_SEAL_500 0xFF00A8CCu /* 标准强调:按钮、选中态 */
#define DP_SEAL_300 0xFF44CCF0u /* 高亮强调:焦点环 */
#define DP_SEAL_100 0xFF99E8FFu /* 亮强调:发光边缘 */
/* --- 辅助色:弧光紫 (Arc Violet) — 次级/装饰 --- */
#define DP_ARC_900 0xFF2A1040u
#define DP_ARC_700 0xFF5A2080u
#define DP_ARC_500 0xFF8844CCu /* 装饰强调:渐变终点、图标 */
#define DP_ARC_300 0xFFB888E8u
#define DP_ARC_100 0xFFE0C8F8u
/* --- 中性色 (Neutral) --- */
#define DP_NEUTRAL_0 0xFF000000u /* 纯黑 */
#define DP_NEUTRAL_50 0xFF181820u /* 接近黑 */
#define DP_NEUTRAL_100 0xFF2A2A38u /* 暗灰 */
#define DP_NEUTRAL_200 0xFF484860u /* 中暗灰 */
#define DP_NEUTRAL_300 0xFF6A6A88u /* 中灰 */
#define DP_NEUTRAL_400 0xFF9090A8u /* 中亮灰 */
#define DP_NEUTRAL_500 0xFFB0B0C0u /* 标准灰 */
#define DP_NEUTRAL_600 0xFFD0D0DCu /* 亮灰 */
#define DP_NEUTRAL_700 0xFFE8E8F0u /* 浅灰 */
#define DP_NEUTRAL_800 0xFFF0F0F8u /* 接近白 */
#define DP_NEUTRAL_900 0xFFFFFFFFu /* 纯白 */
/* --- 语义辅助色 --- */
#define DP_SUCCESS 0xFF40C880u /* 成功:绿色 */
#define DP_WARNING 0xFFF0A030u /* 警告:琥珀色 */
#define DP_ERROR 0xFFFF4466u /* 错误:红色 */
#define DP_INFO 0xFF4499FFu /* 信息:蓝色 */
/* --- 渐变色 — DSK 加载器 / FirstInit 背景专用 --- */
#define DP_GRAD_TOP 0xFFC8E0F0u /* 淡蓝顶 */
#define DP_GRAD_MID 0xFF8888B0u /* 紫灰中 */
#define DP_GRAD_BOT 0xFF49306Fu /* 深紫底 */
/* --- 欢迎页专用 --- */
#define DP_WELCOME_FG 0xFFE8F2FCu /* 近白浅蓝(欢迎文字) */
/* ===================================================================
* L1 — 语义色值 (Semantic Colors)
*
* 暗色主题(Dark Theme)— Shell / 终端
* 亮色主题(Light Theme)— FirstInit / DSK
* =================================================================== */
/* --- 暗色主题 --- */
#define DS_DARK_BG_PRIMARY DP_ABYSS_900 /* 主背景 */
#define DS_DARK_BG_SECONDARY DP_ABYSS_800 /* 卡片/面板背景 */
#define DS_DARK_BG_TERTIARY DP_ABYSS_700 /* 悬停/输入框 */
#define DS_DARK_BG_INSET DP_ABYSS_600 /* 凹陷区域 */
#define DS_DARK_TEXT_PRIMARY DP_NEUTRAL_700 /* 主文字 */
#define DS_DARK_TEXT_SECONDARY DP_NEUTRAL_500 /* 次级文字 */
#define DS_DARK_TEXT_DIM DP_NEUTRAL_300 /* 注释/禁用 */
#define DS_DARK_TEXT_INVERT DP_ABYSS_900 /* 反色文字(亮背景上) */
#define DS_DARK_ACCENT DP_SEAL_500 /* 主强调 */
#define DS_DARK_ACCENT_LIGHT DP_SEAL_300 /* 高亮强调 */
#define DS_DARK_ACCENT_GLOW DP_SEAL_100 /* 发光 */
#define DS_DARK_DECORATIVE DP_ARC_500 /* 装饰色 */
#define DS_DARK_DECORATIVE_L DP_ARC_300 /* 浅装饰 */
#define DS_DARK_BORDER DP_ABYSS_500 /* 边框 */
#define DS_DARK_BORDER_FOCUS DP_SEAL_300 /* 焦点边框 */
#define DS_DARK_DIVIDER DP_ABYSS_600 /* 分隔线 */
#define DS_DARK_CURSOR DP_SEAL_500 /* 光标 */
#define DS_DARK_PROMPT DP_SEAL_300 /* 提示符 */
#define DS_DARK_SELECTION_BG DP_SEAL_900 /* 选中背景 */
/* --- 亮色主题 --- */
#define DS_LIGHT_BG_PRIMARY DP_NEUTRAL_900 /* 主背景(白) */
#define DS_LIGHT_BG_SECONDARY DP_NEUTRAL_700 /* 卡片背景 */
#define DS_LIGHT_BG_TERTIARY DP_NEUTRAL_600 /* 悬停 */
#define DS_LIGHT_BG_INSET DP_NEUTRAL_500 /* 输入框 */
#define DS_LIGHT_TEXT_PRIMARY DP_ABYSS_900 /* 主文字 */
#define DS_LIGHT_TEXT_SECONDARY DP_ABYSS_500 /* 次级 */
#define DS_LIGHT_TEXT_DIM DP_NEUTRAL_300 /* 注释 */
#define DS_LIGHT_TEXT_INVERT DP_NEUTRAL_900 /* 反色 */
#define DS_LIGHT_ACCENT DP_SEAL_700 /* 主强调(亮色上更深) */
#define DS_LIGHT_ACCENT_LIGHT DP_SEAL_500
#define DS_LIGHT_DECORATIVE DP_ARC_700
#define DS_LIGHT_BORDER DP_NEUTRAL_400
#define DS_LIGHT_BORDER_FOCUS DP_SEAL_500
/* ===================================================================
* 排版规范 (Typography)
* =================================================================== */
/* 字体尺寸族 */
#define DU_FONT_SIZE_XS 10u /* 辅助标注 */
#define DU_FONT_SIZE_SM 14u /* 次要说明 */
#define DU_FONT_SIZE_MD 18u /* 正文(Consolas 18px) */
#define DU_FONT_SIZE_LG 24u /* 小标题 */
#define DU_FONT_SIZE_XL 32u /* 标题 */
#define DU_FONT_SIZE_2XL 48u /* 大标题 / 欢迎文字 */
/* ASCII 位图字体参数(与 ascii_bitmaps.c 对应) */
#define DU_ASCII_CELL_W 11u
#define DU_ASCII_CELL_H 18u
#define DU_ASCII_STEP 12u /* 字符间距(含 1px gap) */
#define DU_ASCII_LINE_H 20u /* 行高(含 2px 行间距) */
/* ===================================================================
* 间距体系 (Spacing)
* =================================================================== */
#define DU_SPACE_XXS 2u
#define DU_SPACE_XS 4u
#define DU_SPACE_SM 8u
#define DU_SPACE_MD 16u
#define DU_SPACE_LG 24u
#define DU_SPACE_XL 32u
#define DU_SPACE_2XL 48u
#define DU_SPACE_3XL 64u
/* ===================================================================
* 圆角体系 (Border Radius) — "Sealed Arc" 签名元素
* =================================================================== */
#define DU_RADIUS_NONE 0u
#define DU_RADIUS_SM 4u /* 小按钮、标签 */
#define DU_RADIUS_MD 8u /* 卡片、输入框 */
#define DU_RADIUS_LG 12u /* 大卡片、面板 */
#define DU_RADIUS_XL 16u /* 模态框 */
#define DU_RADIUS_FULL 9999u /* 完全圆角(药丸形) */
/* ===================================================================
* 边框体系 (Border)
* =================================================================== */
#define DU_BORDER_THIN 1u
#define DU_BORDER_STD 2u
#define DU_BORDER_THICK 3u
/* ===================================================================
* 动画参数 (Animation)
* =================================================================== */
/* 帧延迟(busy-wait 循环次数 ≈ 毫秒级,需根据 CPU 速度校准) */
#define DU_ANIM_DELAY_SLOW 200000u /* 慢速过渡 */
#define DU_ANIM_DELAY_NORMAL 100000u /* 标准动画 */
#define DU_ANIM_DELAY_FAST 40000u /* 快速反馈 */
/* 弧线动画参数(DSK spinner 专用) */
#define DU_SPINNER_RADIUS 48u
#define DU_SPINNER_THICKNESS 4u
#define DU_SPINNER_ARC_GAP 82u /* 每弧宽度(度) */
#define DU_SPINNER_ARC_COUNT 4u
#define DU_SPINNER_SPEED 2u /* 每帧旋转度数 */
#define DU_SPINNER_SPRITE 128u /* 精灵缓冲边长 */
/* 渐变动画步进 */
#define DU_FADE_STEPS 32u /* 淡入/淡出步数 */
#define DU_FADE_STEP_DELAY 8000u /* 每步延迟 */
/* ===================================================================
* 组件尺寸 (Component Sizes)
* =================================================================== */
#define DU_BUTTON_HEIGHT 36u
#define DU_INPUT_HEIGHT 32u
#define DU_CARD_PADDING_X 24u
#define DU_CARD_PADDING_Y 16u
#define DU_CARD_MIN_WIDTH 200u
#define DU_CARD_MAX_WIDTH 480u
#define DU_CURSOR_SIZE 24u /* 鼠标光标边长 */
#define DU_NOTIFICATION_H 40u
/* ===================================================================
* 渲染上下文 (Render Context)
*
* 所有绘制函数通过此上下文访问 framebuffer,避免全局状态。
* =================================================================== */
typedef struct {
du_u32 *fb; /* framebuffer 基址 */
du_u64 width; /* 像素宽 */
du_u64 height; /* 像素高 */
du_u64 pitch; /* 字节行宽 */
} du_context;
/* 初始化渲染上下文 */
static inline void du_context_init(du_context *ctx, du_u64 fb_addr,
du_u64 w, du_u64 h, du_u64 pitch) {
ctx->fb = (du_u32 *)(du_u64)fb_addr;
ctx->width = w;
ctx->height = h;
ctx->pitch = pitch;
}
/* ===================================================================
* L2 — 绘制原语 (Rendering Primitives)
* =================================================================== */
/* 颜色混合:alpha 范围 0–255 */
static inline du_u32 du_blend(du_u32 bg, du_u32 fg, du_u32 alpha) {
du_u32 na = 256 - alpha;
du_u32 r = ((bg & 0xFF) * na + (fg & 0xFF) * alpha) >> 8;
du_u32 g = (((bg >> 8) & 0xFF) * na + ((fg >> 8) & 0xFF) * alpha) >> 8;
du_u32 b = (((bg >> 16) & 0xFF) * na + ((fg >> 16) & 0xFF) * alpha) >> 8;
return 0xFF000000u | (b << 16) | (g << 8) | r;
}
/* 线性插值两色 */
static inline du_u32 du_lerp_color(du_u32 c1, du_u32 c2, du_u32 t) {
return du_blend(c1, c2, t);
}
/* 渐变背景:顶色 → 底色,按 Y 坐标插值 */
static inline du_u32 du_gradient_v(du_u32 top_color, du_u32 bot_color,
du_i64 y, du_i64 total_h) {
if (total_h <= 1) return top_color;
if (y < 0) y = 0;
if (y >= total_h) y = total_h - 1;
du_u32 alpha = (du_u32)(((du_u64)y * 255ULL) / ((du_u64)total_h - 1));
return du_blend(top_color, bot_color, alpha);
}
/* 单像素写(带边界检查) */
static inline void du_pixel(du_context *ctx, du_i64 x, du_i64 y, du_u32 color) {
if (x < 0 || (du_u64)x >= ctx->width || y < 0 || (du_u64)y >= ctx->height) return;
((du_u32 *)((du_u8 *)ctx->fb + (du_u64)y * ctx->pitch))[(du_u64)x] = color;
}
/* 读取像素(带边界检查) */
static inline du_u32 du_pixel_read(du_context *ctx, du_i64 x, du_i64 y) {
if (x < 0 || (du_u64)x >= ctx->width || y < 0 || (du_u64)y >= ctx->height) return 0;
return ((du_u32 *)((du_u8 *)ctx->fb + (du_u64)y * ctx->pitch))[(du_u64)x];
}
/* 矩形填充 */
static inline void du_fill_rect(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, du_i64 h, du_u32 color) {
for (du_i64 r = 0; r < h; r++) {
du_i64 yy = y + r;
if (yy < 0 || (du_u64)yy >= ctx->height) continue;
du_u32 *line = (du_u32 *)((du_u8 *)ctx->fb + (du_u64)yy * ctx->pitch);
for (du_i64 c = 0; c < w; c++) {
du_i64 xx = x + c;
if (xx < 0 || (du_u64)xx >= ctx->width) continue;
line[(du_u64)xx] = color;
}
}
}
/* 渐变矩形填充(垂直渐变) */
static inline void du_fill_rect_gradient(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, du_i64 h,
du_u32 top_color, du_u32 bot_color) {
for (du_i64 r = 0; r < h; r++) {
du_i64 yy = y + r;
if (yy < 0 || (du_u64)yy >= ctx->height) continue;
du_u32 color = du_gradient_v(top_color, bot_color, r, h);
du_u32 *line = (du_u32 *)((du_u8 *)ctx->fb + (du_u64)yy * ctx->pitch);
for (du_i64 c = 0; c < w; c++) {
du_i64 xx = x + c;
if (xx < 0 || (du_u64)xx >= ctx->width) continue;
line[(du_u64)xx] = color;
}
}
}
/* 全屏渐变背景 */
static inline void du_fill_bg_gradient(du_context *ctx,
du_u32 top_color, du_u32 bot_color) {
du_fill_rect_gradient(ctx, 0, 0, (du_i64)ctx->width, (du_i64)ctx->height,
top_color, bot_color);
}
/* 全屏纯色背景 */
static inline void du_fill_bg_solid(du_context *ctx, du_u32 color) {
du_fill_rect(ctx, 0, 0, (du_i64)ctx->width, (du_i64)ctx->height, color);
}
/* ===================================================================
* 圆角矩形 (Rounded Rectangle) — "Sealed Arc" 核心形状
* =================================================================== */
/* 绘制圆角矩形边框(单像素宽) */
static inline void du_rect_outline(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, du_i64 h,
du_u32 color, du_u32 radius) {
/* 四个角的圆弧 + 四条直线 */
du_i64 r = (du_i64)radius;
if (r > w / 2) r = w / 2;
if (r > h / 2) r = h / 2;
/* 顶线 */
for (du_i64 c = r; c < w - r; c++) {
du_pixel(ctx, x + c, y, color);
}
/* 底线 */
for (du_i64 c = r; c < w - r; c++) {
du_pixel(ctx, x + c, y + h - 1, color);
}
/* 左线 */
for (du_i64 rr = r; rr < h - r; rr++) {
du_pixel(ctx, x, y + rr, color);
}
/* 右线 */
for (du_i64 rr = r; rr < h - r; rr++) {
du_pixel(ctx, x + w - 1, y + rr, color);
}
/* 四个圆角 */
for (du_i64 dy = 0; dy <= r; dy++) {
for (du_i64 dx = 0; dx <= r; dx++) {
du_i64 d2 = dx * dx + dy * dy;
if (d2 <= r * r && d2 > (r - 1) * (r - 1)) {
/* 左上 */
du_pixel(ctx, x + r - dx, y + r - dy, color);
/* 右上 */
du_pixel(ctx, x + w - 1 - r + dx, y + r - dy, color);
/* 左下 */
du_pixel(ctx, x + r - dx, y + h - 1 - r + dy, color);
/* 右下 */
du_pixel(ctx, x + w - 1 - r + dx, y + h - 1 - r + dy, color);
}
}
}
}
/* 填充圆角矩形 */
static inline void du_fill_rounded_rect(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, du_i64 h,
du_u32 color, du_u32 radius) {
du_i64 r = (du_i64)radius;
if (r > w / 2) r = w / 2;
if (r > h / 2) r = h / 2;
/* 中间区域(不含角) */
du_fill_rect(ctx, x + r, y, w - 2 * r, h, color);
/* 左侧矩形 */
du_fill_rect(ctx, x, y + r, r, h - 2 * r, color);
/* 右侧矩形 */
du_fill_rect(ctx, x + w - r, y + r, r, h - 2 * r, color);
/* 四个圆角填充 */
for (du_i64 dy = 0; dy < r; dy++) {
for (du_i64 dx = 0; dx < r; dx++) {
if (dx * dx + dy * dy <= r * r) {
/* 左上 */
du_pixel(ctx, x + r - 1 - dx, y + r - 1 - dy, color);
/* 右上 */
du_pixel(ctx, x + w - r + dx, y + r - 1 - dy, color);
/* 左下 */
du_pixel(ctx, x + r - 1 - dx, y + h - r + dy, color);
/* 右下 */
du_pixel(ctx, x + w - r + dx, y + h - r + dy, color);
}
}
}
}
/* 带发光效果的圆角矩形边框(模拟 UTSM 封缄发光) */
static inline void du_rect_glow(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, du_i64 h,
du_u32 glow_color, du_u32 radius,
du_i64 glow_width) {
/* 从外到内逐层降低透明度 */
for (du_i64 i = glow_width; i >= 1; i--) {
du_u32 alpha = (du_u32)(40 + (glow_width - i) * 50 / glow_width);
du_u32 color = du_blend(DP_ABYSS_900, glow_color, alpha);
du_rect_outline(ctx, x - i, y - i, w + 2 * i, h + 2 * i, color, radius + (du_u32)i);
}
/* 核心边框 */
du_rect_outline(ctx, x, y, w, h, glow_color, radius);
}
/* ===================================================================
* 分隔线 (Divider)
* =================================================================== */
static inline void du_divider_h(du_context *ctx, du_i64 x, du_i64 y,
du_i64 width, du_u32 color) {
du_fill_rect(ctx, x, y, width, 1, color);
}
static inline void du_divider_v(du_context *ctx, du_i64 x, du_i64 y,
du_i64 height, du_u32 color) {
du_fill_rect(ctx, x, y, 1, height, color);
}
/* ===================================================================
* ASCII 字符渲染(需要外部提供 g_ascii 位图数组)
*
* 使用方法:包含 ascii_bitmaps.c 后调用 du_draw_char / du_draw_string
* =================================================================== */
/* ASCII 位图数组 — 由 ascii_bitmaps.c 定义(static),在包含该 .c 的编译单元内可见 */
/* 渲染单个 ASCII 字符(灰度位图 + 前景色 alpha 混合) */
static inline void du_draw_char(du_context *ctx, char ch,
du_i64 x, du_i64 y,
du_u32 fg, du_u32 bg) {
du_u32 idx = (du_u32)(ch - ' ');
if (idx > 94) idx = 0;
const du_u8 *glyph = g_ascii[idx];
du_i64 gw = g_ascii_w;
du_i64 gh = g_ascii_h;
for (du_i64 r = 0; r < gh; r++) {
du_i64 yy = y + r;
if (yy < 0 || (du_u64)yy >= ctx->height) continue;
du_u32 *line = (du_u32 *)((du_u8 *)ctx->fb + (du_u64)yy * ctx->pitch);
for (du_i64 c = 0; c < gw; c++) {
du_u8 a = glyph[r * gw + c];
if (a == 0) continue;
du_i64 xx = x + c;
if (xx < 0 || (du_u64)xx >= ctx->width) continue;
line[(du_u64)xx] = (a == 255) ? fg : du_blend(bg, fg, a);
}
}
}
/* 渲染字符串(不换行,无自动折行) */
static inline void du_draw_string(du_context *ctx, const char *s,
du_i64 x, du_i64 y,
du_u32 fg, du_u32 bg,
du_u32 step) {
du_i64 cx = x;
while (*s) {
du_draw_char(ctx, *s, cx, y, fg, bg);
cx += (du_i64)step;
s++;
}
}
/* ===================================================================
* 组件令牌 — 按钮 (Button)
* =================================================================== */
typedef struct {
du_u32 bg_normal; /* 正常背景 */
du_u32 bg_hover; /* 悬停背景 */
du_u32 bg_active; /* 按下背景 */
du_u32 fg; /* 文字色 */
du_u32 border; /* 边框色 */
du_u32 border_focus; /* 焦点边框色 */
du_u32 radius; /* 圆角半径 */
du_u32 height; /* 高度 */
du_u32 padding_x; /* 水平内边距 */
} du_button_style;
/* 暗色主题按钮样式 */
#define DU_BTN_DARK_PRIMARY { \
DS_DARK_ACCENT, DS_DARK_ACCENT_LIGHT, DP_SEAL_700, \
DP_NEUTRAL_900, DP_SEAL_300, DP_SEAL_100, \
DU_RADIUS_MD, DU_BUTTON_HEIGHT, DU_SPACE_LG \
}
#define DU_BTN_DARK_SECONDARY { \
DS_DARK_BG_TERTIARY, DS_DARK_BG_INSET, DS_DARK_BG_INSET, \
DS_DARK_TEXT_PRIMARY, DS_DARK_BORDER, DS_DARK_BORDER_FOCUS, \
DU_RADIUS_MD, DU_BUTTON_HEIGHT, DU_SPACE_LG \
}
/* ===================================================================
* 组件令牌 — 卡片 (Card)
* =================================================================== */
typedef struct {
du_u32 bg; /* 卡片背景 */
du_u32 border; /* 边框色 */
du_u32 border_focus; /* 焦点边框色 */
du_u32 shadow_color; /* 阴影色 */
du_u32 radius; /* 圆角半径 */
du_u32 padding_x; /* 水平内边距 */
du_u32 padding_y; /* 垂直内边距 */
du_u32 border_width; /* 边框宽度 */
} du_card_style;
#define DU_CARD_DARK { \
DS_DARK_BG_SECONDARY, DS_DARK_BORDER, DS_DARK_BORDER_FOCUS, \
DP_ABYSS_900, DU_RADIUS_LG, DU_CARD_PADDING_X, DU_CARD_PADDING_Y, DU_BORDER_STD \
}
#define DU_CARD_LIGHT { \
DS_LIGHT_BG_SECONDARY, DS_LIGHT_BORDER, DS_LIGHT_BORDER_FOCUS, \
DP_NEUTRAL_300, DU_RADIUS_LG, DU_CARD_PADDING_X, DU_CARD_PADDING_Y, DU_BORDER_STD \
}
/* ===================================================================
* 组件令牌 — 输入框 (Input)
* =================================================================== */
typedef struct {
du_u32 bg;
du_u32 bg_focus;
du_u32 fg;
du_u32 placeholder;
du_u32 border;
du_u32 border_focus;
du_u32 cursor;
du_u32 radius;
du_u32 height;
} du_input_style;
#define DU_INPUT_DARK { \
DS_DARK_BG_INSET, DS_DARK_BG_TERTIARY, DS_DARK_TEXT_PRIMARY, \
DS_DARK_TEXT_DIM, DS_DARK_BORDER, DS_DARK_BORDER_FOCUS, \
DS_DARK_CURSOR, DU_RADIUS_MD, DU_INPUT_HEIGHT \
}
/* ===================================================================
* 组件令牌 — 通知栏 (Notification)
* =================================================================== */
typedef enum {
DU_NOTIFY_INFO,
DU_NOTIFY_SUCCESS,
DU_NOTIFY_WARNING,
DU_NOTIFY_ERROR
} du_notify_type;
static inline du_u32 du_notify_color(du_notify_type type) {
switch (type) {
case DU_NOTIFY_SUCCESS: return DP_SUCCESS;
case DU_NOTIFY_WARNING: return DP_WARNING;
case DU_NOTIFY_ERROR: return DP_ERROR;
default: return DP_INFO;
}
}
/* ===================================================================
* 组件绘制函数 (Component Drawing)
* =================================================================== */
/* 绘制按钮 */
static inline void du_draw_button(du_context *ctx,
du_i64 x, du_i64 y, du_i64 w,
const du_button_style *style,
const char *label, int focused) {
du_u32 bg = style->bg_normal;
du_u32 border = style->border;
if (focused) {
border = style->border_focus;
}
/* 填充圆角矩形背景 */
du_fill_rounded_rect(ctx, x, y, w, (du_i64)style->height, bg, style->radius);
/* 边框 */
du_rect_outline(ctx, x, y, w, (du_i64)style->height, border, style->radius);
/* 文字居中 */
du_i64 label_len = 0;
while (label[label_len]) label_len++;
du_i64 text_w = label_len * (du_i64)DU_ASCII_STEP;
du_i64 text_x = x + (w - text_w) / 2;
du_i64 text_y = y + ((du_i64)style->height - (du_i64)DU_ASCII_CELL_H) / 2;
du_draw_string(ctx, label, text_x, text_y, style->fg, bg, DU_ASCII_STEP);
}
/* 绘制卡片 */
static inline void du_draw_card(du_context *ctx,
du_i64 x, du_i64 y, du_i64 w, du_i64 h,
const du_card_style *style, int focused) {
du_u32 border = style->border;
if (focused) {
border = style->border_focus;
/* 焦点发光效果 */
du_rect_glow(ctx, x - 2, y - 2, w + 4, h + 4,
border, style->radius + 2, 3);
}
du_fill_rounded_rect(ctx, x, y, w, h, style->bg, style->radius);
du_rect_outline(ctx, x, y, w, h, border, style->radius);
}
/* 绘制输入框 */
static inline void du_draw_input(du_context *ctx,
du_i64 x, du_i64 y, du_i64 w,
const du_input_style *style,
const char *text, int cursor_pos,
int focused) {
du_u32 bg = focused ? style->bg_focus : style->bg;
du_u32 border = focused ? style->border_focus : style->border;
du_fill_rounded_rect(ctx, x, y, w, (du_i64)style->height, bg, style->radius);
du_rect_outline(ctx, x, y, w, (du_i64)style->height, border, style->radius);
/* 文字 */
du_i64 text_x = x + (du_i64)DU_SPACE_SM;
du_i64 text_y = y + ((du_i64)style->height - (du_i64)DU_ASCII_CELL_H) / 2;
du_u32 fg = text[0] ? style->fg : style->placeholder;
du_draw_string(ctx, text, text_x, text_y, fg, bg, DU_ASCII_STEP);
/* 光标 */
if (focused && cursor_pos >= 0) {
du_i64 cx = text_x + (du_i64)cursor_pos * (du_i64)DU_ASCII_STEP;
du_fill_rect(ctx, cx, text_y, 2, (du_i64)DU_ASCII_CELL_H, style->cursor);
}
}
/* 绘制通知栏 */
static inline void du_draw_notification(du_context *ctx,
du_i64 x, du_i64 y, du_i64 w,
du_notify_type type,
const char *message) {
du_u32 accent = du_notify_color(type);
/* 半透明背景 */
du_fill_rounded_rect(ctx, x, y, w, (du_i64)DU_NOTIFICATION_H,
DP_ABYSS_800, DU_RADIUS_MD);
/* 左侧色条 */
du_fill_rect(ctx, x, y + 4, 4, (du_i64)DU_NOTIFICATION_H - 8, accent);
/* 文字 */
du_i64 text_y = y + ((du_i64)DU_NOTIFICATION_H - (du_i64)DU_ASCII_CELL_H) / 2;
du_draw_string(ctx, message, x + (du_i64)DU_SPACE_MD, text_y,
DS_DARK_TEXT_PRIMARY, DP_ABYSS_800, DU_ASCII_STEP);
}
/* 绘制进度条 */
static inline void du_draw_progress(du_context *ctx,
du_i64 x, du_i64 y, du_i64 w, du_i64 h,
du_u32 progress_0_255) {
/* 背景 */
du_fill_rounded_rect(ctx, x, y, w, h, DP_ABYSS_700, DU_RADIUS_FULL);
/* 填充 */
du_i64 fill_w = (w * (du_i64)progress_0_255) / 255;
if (fill_w > 0) {
du_fill_rounded_rect(ctx, x, y, fill_w, h, DP_SEAL_500, DU_RADIUS_FULL);
}
}
/* ===================================================================
* Spinner(加载弧) — DSK 启动加载动画
*
* 使用 "Sealed Arc" 四弧旋转设计,弧色取自色板。
* =================================================================== */
/* 默认弧色(从亮到暗的青蓝色) */
#define DU_SPINNER_ARCS { \
DP_SEAL_100, DP_SEAL_300, DP_SEAL_500, DP_SEAL_700 \
}
/* ===================================================================
* 主题切换辅助 (Theme Switch)
* =================================================================== */
typedef enum {
DU_THEME_DARK,
DU_THEME_LIGHT
} du_theme;
/* 根据主题获取对应的背景色 */
static inline du_u32 du_theme_bg(du_theme t) {
return (t == DU_THEME_DARK) ? DS_DARK_BG_PRIMARY : DS_LIGHT_BG_PRIMARY;
}
/* 根据主题获取对应的文字色 */
static inline du_u32 du_theme_text(du_theme t) {
return (t == DU_THEME_DARK) ? DS_DARK_TEXT_PRIMARY : DS_LIGHT_TEXT_PRIMARY;
}
/* 根据主题获取对应的强调色 */
static inline du_u32 du_theme_accent(du_theme t) {
return (t == DU_THEME_DARK) ? DS_DARK_ACCENT : DS_LIGHT_ACCENT;
}
#endif /* DESHAB_UI_H */
+116 -2
View File
@@ -2,12 +2,126 @@
#define UTSM_DRR_H
#include <utsm/types.h>
#include <utsm/config.h>
void drr_stub_init(void);
/* ===================================================================
* DRR — Dedicated Recovery Root
*
* 专用恢复根,独立于普通调度器和堆分配器。
* 负责:root key、recovery log、checkpoint A/B、Emergency Pool。
*
* 约束:
* - DRR recovery 路径禁止依赖普通堆分配器
* - 普通 OOM 不影响 DRR recovery
* - Emergency Pool 耗尽时进入 system rollback
* =================================================================== */
/* ---- Checkpoint 类型 ---- */
typedef enum {
DRR_CKPT_LIGHT = 0, /* 控制面快照 */
DRR_CKPT_DIRTY_PAGE, /* dirty ciphertext page 快照 */
DRR_CKPT_BOOT /* 启动状态 + A/B slot */
} drr_checkpoint_type;
/* ---- Recovery Log 条目 ---- */
typedef struct {
u32 segment_slot;
u32 flags; /* 0=intent, 1=commit */
u64 offset;
u64 length;
u64 timestamp; /* writer_seq snapshot */
} drr_log_entry;
#define DRR_LOG_FLAG_INTENT 0u
#define DRR_LOG_FLAG_COMMIT 1u
/* ---- Checkpoint Metadata Slot ---- */
typedef struct {
u64 magic; /* DRR_CKPT_MAGIC */
u32 version;
u32 active; /* 0=inactive, 1=active */
drr_checkpoint_type type;
u64 global_epoch;
u64 dirty_page_count;
u64 mac_root; /* MAC root hash of all sealed pages */
u64 segment_count;
u64 crc; /* CRC64 of the above */
} drr_checkpoint_meta;
#define DRR_CKPT_MAGIC 0x445252434B505421ULL /* "DRRCKPT!" */
#define DRR_CKPT_VERSION 1u
/* ---- Emergency Pool ---- */
#define DRR_EMERGENCY_POOL_SIZE (64ULL * 1024ULL) /* 64KB 独立池 */
#define DRR_EMERGENCY_STACK_SIZE 4096ULL
#define DRR_RECOVERY_LOG_SIZE 4096ULL
#define DRR_CKPT_META_BUF_SIZE 4096ULL
#define DRR_CRASH_BUF_SIZE 2048ULL
#define DRR_META_SLAB_SIZE 2048ULL
#define DRR_EMERGENCY_PAGES 4
typedef struct {
u8 pool[DRR_EMERGENCY_POOL_SIZE] __attribute__((aligned(64)));
u64 pool_offset;
u64 pool_limit;
/* 子缓冲区指针(pool 内偏移分配) */
u8 *stack_base;
u64 stack_size;
u64 stack_ptr;
drr_log_entry *log_base;
u32 log_count;
u32 log_capacity;
drr_checkpoint_meta *ckpt_meta_buf;
u8 *crash_buf;
u64 crash_buf_size;
void *meta_slab;
u64 meta_slab_offset;
/* A/B checkpoint slots */
drr_checkpoint_meta ckpt_a;
drr_checkpoint_meta ckpt_b;
u32 active_slot; /* 0=A, 1=B */
/* Root key */
const u64 *root_key;
/* Recovery generation */
u64 recovery_generation;
/* Heartbeat */
u64 heartbeat_counter;
u64 heartbeat_interval;
/* 初始化标记 */
int initialized;
} drr_state;
/* ---- 公共接口 ---- */
void drr_init(void);
const u64 *drr_get_root_key(void);
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 */
+64 -3
View File
@@ -3,16 +3,77 @@
#include <utsm/types.h>
#include <utsm/uuid.h>
#include <utsm/capability.h>
#include <utsm/config.h>
/* ---- 进程标志 ---- */
#define UTSM_PROC_F_BOOTSTRAP (1U << 0) /* 内核 bootstrap 进程 */
#define UTSM_PROC_F_KERNEL (1U << 1) /* 内核态进程 */
#define UTSM_PROC_F_ENCRYPTED (1U << 2) /* 加密堆栈/堆 */
/* ---- 进程状态 ---- */
typedef enum {
UTSM_PROC_UNUSED = 0,
UTSM_PROC_ACTIVE,
UTSM_PROC_BLOCKED,
UTSM_PROC_TERMINATED
} utsm_process_state;
/* ---- Capability Table(每进程) ---- */
typedef struct {
utsm_capability caps[UTSM_MAX_CAPABILITIES];
u32 count;
u32 generation; /* 表版本号 */
} utsm_cap_table;
/* ---- Crypto Context(每进程) ---- */
typedef struct {
u64 crypto_epoch; /* 当前加密纪元 */
u64 key_derivation_seed; /* 密钥派生种子 */
u32 pckc_hot_hint; /* PCKC 热段提示 */
u32 reserved;
} utsm_crypto_context;
/* ---- 进程上下文 ---- */
typedef struct utsm_process_context {
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 */
+3 -3
View File
@@ -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();
+25
View File
@@ -0,0 +1,25 @@
CC := clang
LD := ld.lld
ROOT := ../..
OUT := $(ROOT)/SYSTEM/system/deshab64/desktop.elf
CFLAGS := -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fpie -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -I../UTSM/include -O2
LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld
C_SOURCES := main.c
OBJECTS := $(C_SOURCES:.c=.o)
.PHONY: all clean
all: $(OUT)
$(OUT): $(OBJECTS) linker.ld
@if not exist "$(ROOT)\SYSTEM\system\deshab64" mkdir "$(ROOT)\SYSTEM\system\deshab64"
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }"
+16
View File
@@ -0,0 +1,16 @@
ENTRY(dsk_entry)
PHDRS
{
text PT_LOAD FLAGS(7);
rodata PT_LOAD FLAGS(4);
data PT_LOAD FLAGS(7);
}
SECTIONS
{
. = 0x1000;
.text : ALIGN(4K) { *(.text*) } :text
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
.data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data
}
+1438
View File
@@ -0,0 +1,1438 @@
/* Deshab Desktop Manager — 图形桌面环境
*
* 功能:窗口管理、任务栏、桌面图标、事件路由、应用加载
* 风格:Sealed Arc 视觉设计语言
* 输入:PS/2 键盘 + PS/2 鼠标
*/
#include "../UTSM/include/utsm/dsk.h"
/* block_read 函数类型(从 kernel_api + 0xA8 获取) */
typedef int (*desktop_block_read_fn)(void *ctx, unsigned long long lba, unsigned int count, void *buffer);
/* 先包含 ascii_bitmaps.c(定义 g_ascii),再包含 deshab_ui.h(引用 g_ascii) */
#include "../firstInit/ascii_bitmaps.c"
#include "../UTSM/include/utsm/deshab_ui.h"
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef long long i64;
#define COM1 0x3F8
static __inline__ void outb(u16 port, u8 value) { __asm__ volatile("outb %0,%1"::"a"(value),"Nd"(port)); }
static __inline__ u8 inb(u16 port) { u8 v; __asm__ volatile("inb %1,%0":"=a"(v):"Nd"(port)); return v; }
static void sputc(char c) {
for (unsigned int i=0; i<100000; i++) { if (inb(COM1+5)&0x20) break; }
outb(COM1, (unsigned char)c);
}
static void swrite(const char *s) { while(*s) { if(*s=='\n')sputc('\r'); sputc(*s++); } }
static void slog(const char *s) { swrite("[desktop] "); swrite(s); swrite("\n"); }
/* ============================================================
* 常量
* ============================================================ */
#define MAX_WINDOWS 8
#define TASKBAR_H 40
#define TITLEBAR_H 28
#define BORDER_W 2
#define DESKTOP_ICON_W 80
#define DESKTOP_ICON_H 72
#define ICON_GUTTER_X 16
#define ICON_GUTTER_Y 16
#define CURSOR_SIZE 24
/* ============================================================
* 事件系统
* ============================================================ */
typedef enum {
EV_MOUSE_MOVE = 1,
EV_MOUSE_DOWN,
EV_MOUSE_UP,
EV_KEY_DOWN,
EV_KEY_UP,
} ev_type;
typedef struct {
ev_type type;
int mx, my; /* 鼠标全局坐标 */
int button; /* 0=左, 1=右 */
u8 scancode;
int shift, ctrl, alt;
} desktop_event;
/* ============================================================
* 窗口
* ============================================================ */
typedef struct desktop_window {
int id;
int x, y, w, h;
int visible;
int focused;
int minimized;
char title[48];
int app_id; /* 关联应用 ID */
void *app_state; /* 应用私有状态 */
int closing; /* 关闭中标记 */
int dirty; /* 需要重绘 */
} desktop_window;
/* ============================================================
* 应用描述符
* ============================================================ */
/* 前向声明 */
struct desktop_window;
typedef struct desktop_window desktop_win;
typedef struct app_ctx {
du_context *fb;
desktop_win *win;
int client_x, client_y, client_w, client_h;
int should_exit;
desktop_block_read_fn block_read;
const void *kernel_api;
} app_ctx;
typedef void *(*app_create_fn)(app_ctx *ctx);
typedef void (*app_event_fn)(void *state, app_ctx *ctx, desktop_event *ev);
typedef void (*app_draw_fn)(void *state, app_ctx *ctx);
typedef void (*app_destroy_fn)(void *state);
typedef struct {
const char *name;
const char *display_name;
u32 default_w, default_h;
app_create_fn on_create;
app_event_fn on_event;
app_draw_fn on_draw;
app_destroy_fn on_destroy;
} app_descriptor;
/* ============================================================
* 全局状态
* ============================================================ */
static du_context g_fb;
static u64 g_fb_addr, g_fb_w, g_fb_h, g_fb_pitch;
static const void *g_kernel_api;
static desktop_block_read_fn g_block_read;
/* 鼠标状态 */
static int g_mouse_x = 400, g_mouse_y = 300;
static int g_mouse_btn = 0; /* bit0=左键 */
static u8 g_mouse_buf[3];
static int g_mouse_idx = 0;
static int g_mouse_has_pkt = 0;
/* 光标背景保存 */
static u32 g_cursor_save[CURSOR_SIZE * CURSOR_SIZE];
static int g_cursor_saved = 0;
static int g_cursor_old_x = -1, g_cursor_old_y = -1;
/* 键盘状态 */
static int g_shift = 0, g_ctrl = 0, g_alt = 0;
static int g_e0 = 0;
/* 窗口列表 */
static desktop_window g_windows[MAX_WINDOWS];
static int g_win_count = 0;
static int g_next_win_id = 1;
static int g_focused_win = -1;
/* 拖拽状态 */
static int g_dragging = 0;
static int g_drag_win = -1;
static int g_drag_off_x, g_drag_off_y;
/* 应用表 */
#define MAX_APPS 8
static app_descriptor g_apps[MAX_APPS];
static int g_app_count = 0;
/* 桌面图标 */
typedef struct {
int app_id;
int x, y; /* 图标位置 */
const char *label;
} desktop_icon;
#define MAX_ICONS 8
static desktop_icon g_icons[MAX_ICONS];
static int g_icon_count = 0;
/* ============================================================
* 箭头光标形状(24×24,1=前景 0=透明)
* ============================================================ */
static const u8 cursor_shape[24][24] = {
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
};
/* ============================================================
* 应用图标形状(32×32 像素数据,用程序化方式生成)
* ============================================================ */
/* 绘制终端图标(32×32)到指定位置 */
static void draw_icon_terminal(du_context *ctx, int cx, int cy) {
u32 bg = DP_ABYSS_700;
u32 fg = DP_SEAL_300;
u32 border = DP_SEAL_500;
/* 圆角矩形背景 */
du_fill_rounded_rect(ctx, cx, cy, 32, 32, bg, 6);
du_rect_outline(ctx, cx, cy, 32, 32, border, 6);
/* >_ 符号 */
du_fill_rect(ctx, cx+5, cy+10, 8, 2, fg);
du_fill_rect(ctx, cx+5, cy+10, 2, 6, fg);
du_fill_rect(ctx, cx+13, cy+12, 2, 2, fg);
du_fill_rect(ctx, cx+17, cy+16, 6, 2, fg);
}
/* 绘制编辑器图标(32×32) */
static void draw_icon_editor(du_context *ctx, int cx, int cy) {
u32 bg = DP_ABYSS_700;
u32 fg = DP_SEAL_500;
u32 border = DP_SEAL_300;
du_fill_rounded_rect(ctx, cx, cy, 32, 32, bg, 6);
du_rect_outline(ctx, cx, cy, 32, 32, border, 6);
/* 文本行 */
for (int i = 0; i < 5; i++) {
int lw = (i == 0) ? 20 : (i == 3 ? 12 : 16);
du_fill_rect(ctx, cx+6, cy+6+i*5, lw, 2, fg);
}
}
/* 绘制文件夹图标(32×32) */
static void draw_icon_folder(du_context *ctx, int cx, int cy) {
u32 bg = DP_ARC_500;
u32 fg = DP_ARC_300;
u32 border = DP_ARC_700;
du_fill_rounded_rect(ctx, cx, cy, 32, 32, bg, 6);
du_rect_outline(ctx, cx, cy, 32, 32, border, 6);
/* 文件夹标签 */
du_fill_rect(ctx, cx+4, cy+6, 12, 3, fg);
/* 文件夹主体 */
du_fill_rect(ctx, cx+4, cy+12, 24, 2, border);
du_fill_rect(ctx, cx+4, cy+14, 24, 12, fg);
}
/* 绘制计算器图标(32×32) */
static void draw_icon_calc(du_context *ctx, int cx, int cy) {
u32 bg = DP_ABYSS_700;
u32 fg = DP_WARNING;
u32 border = DP_WARNING;
du_fill_rounded_rect(ctx, cx, cy, 32, 32, bg, 6);
du_rect_outline(ctx, cx, cy, 32, 32, border, 6);
/* 屏幕 */
du_fill_rect(ctx, cx+4, cy+4, 24, 6, DP_ABYSS_900);
du_fill_rect(ctx, cx+22, cy+5, 4, 4, fg);
/* 按键网格 */
for (int r = 0; r < 3; r++) {
for (int c = 0; c < 3; c++) {
du_fill_rect(ctx, cx+4+c*8, cy+14+r*6, 6, 4, DP_NEUTRAL_400);
}
}
}
/* ============================================================
* 光标管理
* ============================================================ */
static void cursor_save_bg(int mx, int my) {
for (int r = 0; r < CURSOR_SIZE; r++) {
for (int c = 0; c < CURSOR_SIZE; c++) {
int x = mx + c, y = my + r;
if (x >= 0 && (u64)x < g_fb_w && y >= 0 && (u64)y < g_fb_h) {
u32 *line = (u32 *)((u8 *)g_fb.fb + (u64)y * g_fb.pitch);
g_cursor_save[r * CURSOR_SIZE + c] = line[x];
} else {
g_cursor_save[r * CURSOR_SIZE + c] = 0;
}
}
}
g_cursor_saved = 1;
g_cursor_old_x = mx;
g_cursor_old_y = my;
}
static void cursor_restore_bg(void) {
if (!g_cursor_saved) return;
for (int r = 0; r < CURSOR_SIZE; r++) {
for (int c = 0; c < CURSOR_SIZE; c++) {
int x = g_cursor_old_x + c, y = g_cursor_old_y + r;
if (x >= 0 && (u64)x < g_fb_w && y >= 0 && (u64)y < g_fb_h) {
u32 *line = (u32 *)((u8 *)g_fb.fb + (u64)y * g_fb.pitch);
line[x] = g_cursor_save[r * CURSOR_SIZE + c];
}
}
}
g_cursor_saved = 0;
}
static void cursor_draw(int mx, int my) {
for (int r = 0; r < CURSOR_SIZE; r++) {
for (int c = 0; c < CURSOR_SIZE; c++) {
if (cursor_shape[r][c]) {
du_pixel(&g_fb, mx + c, my + r, DS_DARK_TEXT_PRIMARY);
}
}
}
}
/* ============================================================
* 窗口管理
* ============================================================ */
static desktop_window *win_find(int id) {
for (int i = 0; i < g_win_count; i++) {
if (g_windows[i].id == id) return &g_windows[i];
}
return 0;
}
static desktop_window *win_find_at(int mx, int my) {
/* 从后往前搜索(后面的窗口在上面) */
for (int i = g_win_count - 1; i >= 0; i--) {
desktop_window *w = &g_windows[i];
if (!w->visible || w->minimized) continue;
if (mx >= w->x && mx < w->x + w->w && my >= w->y && my < w->y + w->h) {
return w;
}
}
return 0;
}
static void win_bring_to_front(desktop_window *w) {
int idx = -1;
for (int i = 0; i < g_win_count; i++) {
if (g_windows[i].id == w->id) { idx = i; break; }
}
if (idx < 0 || idx == g_win_count - 1) return;
desktop_window tmp = g_windows[idx];
for (int i = idx; i < g_win_count - 1; i++) {
g_windows[i] = g_windows[i+1];
}
g_windows[g_win_count - 1] = tmp;
}
static desktop_window *win_create(int app_id, const char *title, int x, int y, int w, int h) {
if (g_win_count >= MAX_WINDOWS) return 0;
desktop_window *win = &g_windows[g_win_count++];
int id = g_next_win_id++;
win->id = id;
win->x = x; win->y = y; win->w = w; win->h = h;
win->visible = 1;
win->focused = 1;
win->minimized = 0;
win->app_id = app_id;
win->app_state = 0;
win->closing = 0;
win->dirty = 1;
/* 拷贝标题 */
for (int i = 0; i < 47 && title[i]; i++) win->title[i] = title[i];
win->title[47] = 0;
/* 取消其他窗口焦点 */
for (int i = 0; i < g_win_count - 1; i++) g_windows[i].focused = 0;
g_focused_win = id;
return win;
}
static void win_destroy(desktop_window *w) {
int idx = -1;
for (int i = 0; i < g_win_count; i++) {
if (g_windows[i].id == w->id) { idx = i; break; }
}
if (idx < 0) return;
/* 调用应用的 on_destroy */
if (w->app_id >= 0 && w->app_id < g_app_count && g_apps[w->app_id].on_destroy && w->app_state) {
g_apps[w->app_id].on_destroy(w->app_state);
}
for (int i = idx; i < g_win_count - 1; i++) g_windows[i] = g_windows[i+1];
g_win_count--;
if (g_focused_win == w->id) {
g_focused_win = g_win_count > 0 ? g_windows[g_win_count-1].id : -1;
if (g_focused_win >= 0) win_find(g_focused_win)->focused = 1;
}
}
static int win_hit_titlebar(desktop_window *w, int mx, int my) {
return mx >= w->x && mx < w->x + w->w &&
my >= w->y && my < w->y + TITLEBAR_H;
}
static int win_hit_close_btn(desktop_window *w, int mx, int my) {
int bx = w->x + w->w - 28;
int by = w->y + 4;
return mx >= bx && mx < bx + 24 && my >= by && my < by + 20;
}
/* ============================================================
* 窗口装饰绘制
* ============================================================ */
static void draw_window_frame(desktop_window *w) {
if (!w->visible || w->minimized) return;
u32 titlebar_bg = w->focused ? DP_ABYSS_600 : DP_ABYSS_800;
u32 border_col = w->focused ? DS_DARK_BORDER_FOCUS : DS_DARK_BORDER;
/* 发光效果(焦点窗口) */
if (w->focused) {
du_rect_glow(&g_fb, w->x-2, w->y-2, w->w+4, w->h+4,
DS_DARK_BORDER_FOCUS, 10, 3);
}
/* 窗口主体背景 */
du_fill_rounded_rect(&g_fb, w->x, w->y, w->w, w->h,
DS_DARK_BG_SECONDARY, DU_RADIUS_LG);
/* 标题栏 */
du_fill_rect(&g_fb, w->x + BORDER_W, w->y + BORDER_W,
w->w - 2*BORDER_W, TITLEBAR_H - BORDER_W, titlebar_bg);
/* 标题文字 */
du_draw_string(&g_fb, w->title,
w->x + (i64)DU_SPACE_SM,
w->y + (TITLEBAR_H - (i64)DU_ASCII_CELL_H) / 2,
DS_DARK_TEXT_PRIMARY, titlebar_bg, DU_ASCII_STEP);
/* 关闭按钮 */
int bx = w->x + w->w - 28;
int by = w->y + 4;
du_fill_rounded_rect(&g_fb, bx, by, 24, 20, DP_ERROR, DU_RADIUS_SM);
du_draw_string(&g_fb, "X", bx + 8, by + 1, DP_NEUTRAL_900, DP_ERROR, DU_ASCII_STEP);
/* 边框 */
du_rect_outline(&g_fb, w->x, w->y, w->w, w->h, border_col, DU_RADIUS_LG);
/* 标题栏底线 */
du_fill_rect(&g_fb, w->x + BORDER_W, w->y + TITLEBAR_H,
w->w - 2*BORDER_W, 1, border_col);
}
/* ============================================================
* 任务栏绘制
* ============================================================ */
static void draw_taskbar(void) {
i64 tb_y = (i64)g_fb_h - TASKBAR_H;
du_fill_rect(&g_fb, 0, tb_y, (i64)g_fb_w, TASKBAR_H, DP_ABYSS_800);
/* 顶部强调线 */
du_fill_rect(&g_fb, 0, tb_y, (i64)g_fb_w, 2, DS_DARK_ACCENT);
/* Start 按钮 */
du_fill_rounded_rect(&g_fb, (i64)DU_SPACE_SM, tb_y + 4, 48, 32,
DP_ABYSS_600, DU_RADIUS_SM);
du_draw_string(&g_fb, "D",
(i64)DU_SPACE_SM + 18, tb_y + 8,
DS_DARK_ACCENT_LIGHT, DP_ABYSS_600, DU_ASCII_STEP);
/* 快速启动图标 */
int qx = 64;
for (int i = 0; i < g_app_count && i < 4; i++) {
int icon_x = qx + i * 40;
switch (i) {
case 0: draw_icon_terminal(&g_fb, icon_x + 4, (int)tb_y + 4); break;
case 1: draw_icon_editor(&g_fb, icon_x + 4, (int)tb_y + 4); break;
case 2: draw_icon_folder(&g_fb, icon_x + 4, (int)tb_y + 4); break;
case 3: draw_icon_calc(&g_fb, icon_x + 4, (int)tb_y + 4); break;
}
}
/* 窗口按钮 */
int wx = qx + 4 * 40 + (int)DU_SPACE_LG;
for (int i = 0; i < g_win_count; i++) {
desktop_window *w = &g_windows[i];
if (w->minimized) continue;
u32 btn_bg = w->focused ? DS_DARK_ACCENT : DP_ABYSS_600;
du_fill_rounded_rect(&g_fb, wx, tb_y + 4, 100, 32, btn_bg, DU_RADIUS_SM);
du_draw_string(&g_fb, w->title, wx + 4, tb_y + 8,
DS_DARK_TEXT_PRIMARY, btn_bg, DU_ASCII_STEP);
wx += 104;
if (wx > (int)g_fb_w - 200) break;
}
/* 时钟 */
u8 rtc_h = 0, rtc_m = 0;
outb(0x70, 4); rtc_h = inb(0x71);
outb(0x70, 2); rtc_m = inb(0x71);
/* BCD → 二进制 */
rtc_h = (u8)((rtc_h >> 4) * 10 + (rtc_h & 0xF));
rtc_m = (u8)((rtc_m >> 4) * 10 + (rtc_m & 0xF));
char clock_str[8];
clock_str[0] = '0' + rtc_h / 10;
clock_str[1] = '0' + rtc_h % 10;
clock_str[2] = ':';
clock_str[3] = '0' + rtc_m / 10;
clock_str[4] = '0' + rtc_m % 10;
clock_str[5] = 0;
du_draw_string(&g_fb, clock_str,
(i64)g_fb_w - 5 * (i64)DU_ASCII_STEP - (i64)DU_SPACE_MD,
tb_y + (TASKBAR_H - (i64)DU_ASCII_CELL_H) / 2,
DS_DARK_TEXT_SECONDARY, DP_ABYSS_800, DU_ASCII_STEP);
}
/* ============================================================
* 桌面图标绘制
* ============================================================ */
static void draw_desktop_icons(void) {
for (int i = 0; i < g_icon_count; i++) {
desktop_icon *ic = &g_icons[i];
int cx = ic->x;
int cy = ic->y;
/* 根据应用 ID 画不同图标 */
switch (ic->app_id) {
case 0: draw_icon_terminal(&g_fb, cx, cy); break;
case 1: draw_icon_editor(&g_fb, cx, cy); break;
case 2: draw_icon_folder(&g_fb, cx, cy); break;
case 3: draw_icon_calc(&g_fb, cx, cy); break;
default: draw_icon_terminal(&g_fb, cx, cy); break;
}
/* 标签文字 */
int label_len = 0;
while (ic->label[label_len]) label_len++;
int label_x = cx + (32 - label_len * (int)DU_ASCII_STEP) / 2;
if (label_x < cx) label_x = cx;
du_draw_string(&g_fb, ic->label, label_x, cy + 36,
DS_DARK_TEXT_PRIMARY, DS_DARK_BG_PRIMARY, DU_ASCII_STEP);
}
}
/* ============================================================
* PS/2 鼠标处理
* ============================================================ */
static void ps2_mouse_init(void) {
/* 启用 AUX 设备 */
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
outb(0x64, 0xA8);
/* 启用 AUX IRQ */
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
outb(0x64, 0x20);
for (int t = 0; t < 100000; t++) { if (inb(0x64) & 1) break; }
u8 cfg = inb(0x60);
cfg |= 0x02; /* enable AUX IRQ12 */
cfg &= ~0x20; /* enable AUX clock */
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
outb(0x64, 0x60);
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
outb(0x60, cfg);
/* 复位鼠标 */
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
outb(0x64, 0xD4);
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
outb(0x60, 0xFF);
for (int t = 0; t < 100000; t++) { if (inb(0x64) & 1) break; }
inb(0x60); /* ACK */
/* 启用 streaming */
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
outb(0x64, 0xD4);
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
outb(0x60, 0xF4);
for (int t = 0; t < 100000; t++) { if (inb(0x64) & 1) break; }
inb(0x60); /* ACK */
slog("mouse init ok");
}
static int ps2_mouse_poll(void) {
u8 st = inb(0x64);
if (!(st & 1)) return 0;
u8 data = inb(0x60);
if (!(st & 0x20)) return 0; /* 不是鼠标数据 */
g_mouse_buf[g_mouse_idx++] = data;
if (g_mouse_idx < 3) return 0;
g_mouse_idx = 0;
/* 检查 bit3 同步 */
if (!(g_mouse_buf[0] & 0x08)) return 0;
int dx = (int)(i8)g_mouse_buf[1];
int dy = (int)(i8)g_mouse_buf[2];
if (g_mouse_buf[0] & 0x10) dx = -dx; /* X sign */
/* 注意:Y 轴方向(鼠标 Y 向上为正,屏幕 Y 向下为正) */
if (!(g_mouse_buf[0] & 0x20)) dy = -dy; /* Y sign */
else dy = -dy;
dy = -dy; /* 鼠标 Y 翻转 */
g_mouse_x += dx;
g_mouse_y += dy;
if (g_mouse_x < 0) g_mouse_x = 0;
if (g_mouse_y < 0) g_mouse_y = 0;
if (g_mouse_x >= (int)g_fb_w - CURSOR_SIZE) g_mouse_x = (int)g_fb_w - CURSOR_SIZE;
if (g_mouse_y >= (int)g_fb_h - CURSOR_SIZE) g_mouse_y = (int)g_fb_h - CURSOR_SIZE;
g_mouse_btn = (g_mouse_buf[0] & 0x01) ? 1 : 0;
g_mouse_has_pkt = 1;
return 1;
}
/* ============================================================
* 键盘扫描码 → ASCII
* ============================================================ */
static char scan_to_ascii(u8 sc, int shift) {
static const char normal[58] = {
0, 27, '1','2','3','4','5','6','7','8','9','0','-','=', 8, '\t',
'q','w','e','r','t','y','u','i','o','p','[',']','\n',0,
'a','s','d','f','g','h','j','k','l',';','\'', '`',0,'\\',
'z','x','c','v','b','n','m',',','.','/',0,'*',0,' '
};
static const char shifted[58] = {
0, 27, '!','@','#','$','%','^','&','*','(',')','_','+', 8, '\t',
'Q','W','E','R','T','Y','U','I','O','P','{','}','\n',0,
'A','S','D','F','G','H','J','K','L',':','"','~',0,'|',
'Z','X','C','V','B','N','M','<','>','?',0,'*',0,' '
};
if (sc >= 58) return 0;
return shift ? shifted[sc] : normal[sc];
}
/* ============================================================
* 内置应用:终端 (Bash)
* ============================================================ */
#define TERM_MAX_COLS 80
#define TERM_MAX_ROWS 40
#define TERM_MAX_CHARS (TERM_MAX_COLS * TERM_MAX_ROWS)
typedef struct {
u8 ch[TERM_MAX_CHARS];
u32 fg[TERM_MAX_CHARS];
int cols, rows;
int cur_col, cur_row;
char input_buf[256];
int input_len, input_cursor;
int prompt_len;
} bash_state;
static const char *BASH_PROMPT = "deshab# ";
static void *bash_on_create(app_ctx *ctx) {
bash_state *s = (bash_state *)0x4000000; /* 固定地址分配,避免堆依赖 */
for (int i = 0; i < TERM_MAX_CHARS; i++) {
s->ch[i] = ' '; s->fg[i] = DS_DARK_TEXT_PRIMARY;
}
s->cols = (ctx->client_w - (int)DU_SPACE_SM * 2) / (int)DU_ASCII_STEP;
s->rows = (ctx->client_h - (int)DU_SPACE_SM * 2) / (int)DU_ASCII_LINE_H;
if (s->cols > TERM_MAX_COLS) s->cols = TERM_MAX_COLS;
if (s->rows > TERM_MAX_ROWS) s->rows = TERM_MAX_ROWS;
s->cur_col = 0; s->cur_row = 0;
s->input_len = 0; s->input_cursor = 0;
s->prompt_len = 0;
while (BASH_PROMPT[s->prompt_len]) s->prompt_len++;
/* 打印欢迎 */
const char *welcome = "Deshab Bash v0.1\n输入 help 查看命令\n\n";
while (*welcome) {
if (*welcome == '\n') {
s->cur_col = 0; s->cur_row++;
} else {
if (s->cur_row < s->rows && s->cur_col < s->cols) {
int idx = s->cur_row * TERM_MAX_COLS + s->cur_col;
s->ch[idx] = (u8)*welcome;
s->fg[idx] = DS_DARK_TEXT_PRIMARY;
}
s->cur_col++;
}
welcome++;
}
return s;
}
static void bash_putc(bash_state *s, char c, u32 color) {
if (c == '\n') { s->cur_col = 0; s->cur_row++; return; }
if (s->cur_row >= s->rows) {
/* 滚动 */
for (int r = 1; r < s->rows; r++) {
for (int c2 = 0; c2 < s->cols; c2++) {
s->ch[(r-1)*TERM_MAX_COLS+c2] = s->ch[r*TERM_MAX_COLS+c2];
s->fg[(r-1)*TERM_MAX_COLS+c2] = s->fg[r*TERM_MAX_COLS+c2];
}
}
for (int c2 = 0; c2 < s->cols; c2++) {
s->ch[(s->rows-1)*TERM_MAX_COLS+c2] = ' ';
}
s->cur_row = s->rows - 1;
}
if (s->cur_col >= s->cols) { s->cur_col = 0; s->cur_row++; }
int idx = s->cur_row * TERM_MAX_COLS + s->cur_col;
s->ch[idx] = (u8)c; s->fg[idx] = color;
s->cur_col++;
}
static void bash_puts(bash_state *s, const char *str, u32 color) {
while (*str) bash_putc(s, *str++, color);
}
static void bash_execute(bash_state *s, const char *cmd) {
while (*cmd == ' ') cmd++;
bash_puts(s, BASH_PROMPT, DS_DARK_PROMPT);
bash_puts(s, cmd, DS_DARK_TEXT_PRIMARY);
bash_putc(s, '\n', 0);
if (*cmd == 0) return;
if (cmd[0]=='h'&&cmd[1]=='e'&&cmd[2]=='l'&&cmd[3]=='p') {
bash_puts(s, " help clear echo version\n", DP_SUCCESS);
bash_puts(s, " uname date about reboot\n", DP_SUCCESS);
bash_puts(s, " halt pwd whoami id\n", DP_SUCCESS);
} else if (cmd[0]=='c'&&cmd[1]=='l'&&cmd[2]=='e'&&cmd[3]=='a'&&cmd[4]=='r') {
for (int i = 0; i < TERM_MAX_CHARS; i++) { s->ch[i] = ' '; }
s->cur_col = 0; s->cur_row = 0;
} else if (cmd[0]=='v'&&cmd[1]=='e'&&cmd[2]=='r') {
bash_puts(s, "Deshab OS v0.1.0\n", DP_SUCCESS);
} else if (cmd[0]=='u'&&cmd[1]=='n'&&cmd[2]=='a') {
bash_puts(s, "Deshab\n", DS_DARK_TEXT_PRIMARY);
} else if (cmd[0]=='a'&&cmd[1]=='b'&&cmd[2]=='o'&&cmd[3]=='u'&&cmd[4]=='t') {
bash_puts(s, "Deshab OS - SAS-R0 Kernel\n", DP_SEAL_300);
bash_puts(s, "Sealed Arc UI Design\n", DP_ARC_300);
} else if (cmd[0]=='e'&&cmd[1]=='c'&&cmd[2]=='h'&&cmd[3]=='o') {
const char *arg = cmd + 4;
while (*arg == ' ') arg++;
bash_puts(s, arg, DS_DARK_TEXT_PRIMARY);
bash_putc(s, '\n', 0);
} else if (cmd[0]=='p'&&cmd[1]=='w'&&cmd[2]=='d') {
bash_puts(s, "/\n", DS_DARK_TEXT_PRIMARY);
} else if (cmd[0]=='w'&&cmd[1]=='h'&&cmd[2]=='o') {
bash_puts(s, "root\n", DS_DARK_TEXT_PRIMARY);
} else if (cmd[0]=='i'&&cmd[1]=='d') {
bash_puts(s, "uid=0(root) gid=0(root)\n", DS_DARK_TEXT_PRIMARY);
} else if (cmd[0]=='r'&&cmd[1]=='e'&&cmd[2]=='b') {
bash_puts(s, "Rebooting...\n", DS_DARK_PROMPT);
outb(0x64, 0xFE);
for(;;) __asm__("hlt");
} else if (cmd[0]=='h'&&cmd[1]=='a'&&cmd[2]=='l'&&cmd[3]=='t') {
bash_puts(s, "Halted.\n", DS_DARK_PROMPT);
for(;;) __asm__("hlt");
} else {
bash_puts(s, "unknown: ", DP_ERROR);
bash_puts(s, cmd, DP_ERROR);
bash_putc(s, '\n', 0);
}
}
static void bash_on_event(void *state, app_ctx *ctx, desktop_event *ev) {
bash_state *s = (bash_state *)state;
if (ev->type != EV_KEY_DOWN) return;
u8 sc = ev->scancode;
if (sc == 0x1C) { /* Enter */
s->input_buf[s->input_len] = 0;
bash_execute(s, s->input_buf);
s->input_len = 0; s->input_cursor = 0;
} else if (sc == 0x0E) { /* Backspace */
if (s->input_cursor > 0) {
for (int i = s->input_cursor-1; i < s->input_len-1; i++)
s->input_buf[i] = s->input_buf[i+1];
s->input_len--; s->input_cursor--;
}
} else {
char c = scan_to_ascii(sc, ev->shift);
if (c && c >= 32 && c <= 126 && s->input_len < 255) {
for (int i = s->input_len; i > s->input_cursor; i--)
s->input_buf[i] = s->input_buf[i-1];
s->input_buf[s->input_cursor++] = c;
s->input_len++;
}
}
ctx->win->dirty = 1;
}
static void bash_on_draw(void *state, app_ctx *ctx) {
bash_state *s = (bash_state *)state;
int cx = ctx->client_x;
int cy = ctx->client_y;
int cw = ctx->client_w;
int ch = ctx->client_h;
/* 背景 */
du_fill_rect(&g_fb, cx, cy, cw, ch, DS_DARK_BG_PRIMARY);
/* 渲染所有字符 */
for (int r = 0; r < s->rows; r++) {
for (int c = 0; c < s->cols; c++) {
int idx = r * TERM_MAX_COLS + c;
if (s->ch[idx] == ' ') continue;
du_draw_char(&g_fb, s->ch[idx],
cx + (int)DU_SPACE_SM + c * (int)DU_ASCII_STEP,
cy + (int)DU_SPACE_SM + r * (int)DU_ASCII_LINE_H,
s->fg[idx], DS_DARK_BG_PRIMARY);
}
}
/* 绘制当前输入行 */
int input_y = cy + (int)DU_SPACE_SM + s->cur_row * (int)DU_ASCII_LINE_H;
/* 提示符 */
du_draw_string(&g_fb, BASH_PROMPT,
cx + (int)DU_SPACE_SM, input_y,
DS_DARK_PROMPT, DS_DARK_BG_PRIMARY, DU_ASCII_STEP);
/* 输入内容 */
du_draw_string(&g_fb, s->input_buf,
cx + (int)DU_SPACE_SM + s->prompt_len * (int)DU_ASCII_STEP,
input_y,
DS_DARK_TEXT_PRIMARY, DS_DARK_BG_PRIMARY, DU_ASCII_STEP);
/* 光标 */
int cursor_x = cx + (int)DU_SPACE_SM + (s->prompt_len + s->input_cursor) * (int)DU_ASCII_STEP;
du_fill_rect(&g_fb, cursor_x, input_y + (int)DU_ASCII_CELL_H - 3,
(int)DU_ASCII_CELL_W, 2, DS_DARK_CURSOR);
}
static void bash_on_destroy(void *state) {
(void)state;
}
/* ============================================================
* 内置应用:文本编辑器 (Editor)
* ============================================================ */
#define EDITOR_BUF_SIZE 65536
typedef struct {
char text[EDITOR_BUF_SIZE];
int text_len;
int cursor_pos;
int scroll_y; /* 行偏移 */
int cursor_line;
int cursor_col;
int modified;
char filename[64];
} editor_state;
static void editor_recalc_cursor(editor_state *s) {
s->cursor_line = 0; s->cursor_col = 0;
for (int i = 0; i < s->cursor_pos && i < s->text_len; i++) {
if (s->text[i] == '\n') { s->cursor_line++; s->cursor_col = 0; }
else s->cursor_col++;
}
}
static int editor_line_start(editor_state *s, int line) {
int l = 0;
for (int i = 0; i < s->text_len; i++) {
if (l == line) return i;
if (s->text[i] == '\n') l++;
}
return s->text_len;
}
static void *editor_on_create(app_ctx *ctx) {
editor_state *s = (editor_state *)0x5000000; /* 固定地址 */
s->text_len = 0; s->cursor_pos = 0;
s->scroll_y = 0; s->modified = 0;
s->filename[0] = 0;
/* 默认内容 */
const char *sample = "# Welcome to Deshab Editor\n#\n\nStart typing here...\n";
while (*sample && s->text_len < EDITOR_BUF_SIZE - 1) {
s->text[s->text_len++] = *sample++;
}
s->cursor_pos = s->text_len;
editor_recalc_cursor(s);
/* 设置窗口标题 */
du_draw_string(&g_fb, "", 0, 0, 0, 0, 0); /* 占位 */
for (int i = 0; i < 63; i++) ctx->win->title[i] = 0;
const char *t = "Editor - untitled";
for (int i = 0; t[i] && i < 47; i++) ctx->win->title[i] = t[i];
(void)ctx;
return s;
}
static void editor_on_event(void *state, app_ctx *ctx, desktop_event *ev) {
editor_state *s = (editor_state *)state;
if (ev->type != EV_KEY_DOWN) return;
u8 sc = ev->scancode;
int e0 = 0; /* TODO: 扩展键处理 */
if (sc == 0x1C) { /* Enter */
if (s->text_len < EDITOR_BUF_SIZE - 1) {
for (int i = s->text_len; i > s->cursor_pos; i--)
s->text[i] = s->text[i-1];
s->text[s->cursor_pos] = '\n';
s->text_len++; s->cursor_pos++;
s->modified = 1;
}
} else if (sc == 0x0E) { /* Backspace */
if (s->cursor_pos > 0) {
for (int i = s->cursor_pos - 1; i < s->text_len - 1; i++)
s->text[i] = s->text[i+1];
s->text_len--; s->cursor_pos--;
s->modified = 1;
}
} else if (sc == 0x48) { /* Up */
if (s->cursor_line > 0) {
int prev_start = editor_line_start(s, s->cursor_line - 1);
int col = s->cursor_col;
s->cursor_pos = prev_start;
for (int i = 0; i < col && s->cursor_pos < s->text_len && s->text[s->cursor_pos] != '\n'; i++)
s->cursor_pos++;
}
} else if (sc == 0x50) { /* Down */
if (s->cursor_line < 999) {
int next_start = editor_line_start(s, s->cursor_line + 1);
int col = s->cursor_col;
s->cursor_pos = next_start;
for (int i = 0; i < col && s->cursor_pos < s->text_len && s->text[s->cursor_pos] != '\n'; i++)
s->cursor_pos++;
}
} else if (sc == 0x4B) { /* Left */
if (s->cursor_pos > 0) s->cursor_pos--;
} else if (sc == 0x4D) { /* Right */
if (s->cursor_pos < s->text_len) s->cursor_pos++;
} else if (sc == 0x47) { /* Home */
s->cursor_pos = editor_line_start(s, s->cursor_line);
} else if (sc == 0x4F) { /* End */
s->cursor_pos = editor_line_start(s, s->cursor_line);
while (s->cursor_pos < s->text_len && s->text[s->cursor_pos] != '\n')
s->cursor_pos++;
} else {
char c = scan_to_ascii(sc, ev->shift);
if (c && c >= 32 && c <= 126 && s->text_len < EDITOR_BUF_SIZE - 1) {
for (int i = s->text_len; i > s->cursor_pos; i--)
s->text[i] = s->text[i-1];
s->text[s->cursor_pos] = c;
s->text_len++; s->cursor_pos++;
s->modified = 1;
}
}
editor_recalc_cursor(s);
ctx->win->dirty = 1;
(void)e0;
}
static void editor_on_draw(void *state, app_ctx *ctx) {
editor_state *s = (editor_state *)state;
int cx = ctx->client_x;
int cy = ctx->client_y;
int cw = ctx->client_w;
int ch = ctx->client_h;
int line_num_w = 5 * (int)DU_ASCII_STEP + (int)DU_SPACE_SM;
int text_area_x = cx + line_num_w;
int text_area_w = cw - line_num_w;
int visible_rows = ch / (int)DU_ASCII_LINE_H;
int visible_cols = text_area_w / (int)DU_ASCII_STEP;
/* 背景 */
du_fill_rect(&g_fb, cx, cy, cw, ch, DS_DARK_BG_SECONDARY);
/* 自动滚动 */
if (s->cursor_line < s->scroll_y) s->scroll_y = s->cursor_line;
if (s->cursor_line >= s->scroll_y + visible_rows) s->scroll_y = s->cursor_line - visible_rows + 1;
/* 渲染可见行 */
int line = 0;
int pos = 0;
for (int i = 0; i < s->text_len || line <= s->cursor_line; ) {
if (line >= s->scroll_y + visible_rows) break;
if (line >= s->scroll_y) {
int screen_y = cy + (line - s->scroll_y) * (int)DU_ASCII_LINE_H;
/* 行号 */
char ln[8];
ln[0] = '0' + ((line+1)/100)%10;
ln[1] = '0' + ((line+1)/10)%10;
ln[2] = '0' + (line+1)%10;
ln[3] = 0;
du_draw_string(&g_fb, ln, cx + 2, screen_y,
DS_DARK_TEXT_DIM, DS_DARK_BG_SECONDARY, DU_ASCII_STEP);
/* 行号分隔线 */
du_fill_rect(&g_fb, cx + line_num_w - 2, screen_y, 1,
(int)DU_ASCII_CELL_H, DS_DARK_DIVIDER);
/* 行文本 */
int col = 0;
while (pos < s->text_len && s->text[pos] != '\n' && col < visible_cols) {
du_draw_char(&g_fb, s->text[pos],
text_area_x + col * (int)DU_ASCII_STEP,
screen_y,
DS_DARK_TEXT_PRIMARY, DS_DARK_BG_SECONDARY);
col++; pos++;
}
if (pos < s->text_len && s->text[pos] == '\n') pos++;
} else {
while (pos < s->text_len && s->text[pos] != '\n') pos++;
if (pos < s->text_len) pos++;
}
line++;
if (pos >= s->text_len && line > s->cursor_line) break;
}
/* 光标 */
if (s->cursor_line >= s->scroll_y && s->cursor_line < s->scroll_y + visible_rows) {
int cursor_y = cy + (s->cursor_line - s->scroll_y) * (int)DU_ASCII_LINE_H;
int cursor_x = text_area_x + s->cursor_col * (int)DU_ASCII_STEP;
du_fill_rect(&g_fb, cursor_x, cursor_y, 2, (int)DU_ASCII_CELL_H, DS_DARK_CURSOR);
}
/* 状态栏 */
int status_y = cy + ch - 20;
du_fill_rect(&g_fb, cx, status_y, cw, 20, DP_ABYSS_800);
char status[64];
int sp = 0;
/* 行:列 */
const char *p1 = "L:"; while(*p1) status[sp++] = *p1++;
status[sp++] = '0' + (s->cursor_line+1)/10%10;
status[sp++] = '0' + (s->cursor_line+1)%10;
const char *p2 = " C:"; while(*p2) status[sp++] = *p2++;
status[sp++] = '0' + (s->cursor_col+1)/10%10;
status[sp++] = '0' + (s->cursor_col+1)%10;
if (s->modified) { const char *m = " *"; while(*m) status[sp++] = *m++; }
status[sp] = 0;
du_draw_string(&g_fb, status, cx + 4, status_y + 1,
DS_DARK_TEXT_DIM, DP_ABYSS_800, DU_ASCII_STEP);
}
static void editor_on_destroy(void *state) { (void)state; }
/* ============================================================
* 内置应用:计算器 (Calculator)
* ============================================================ */
typedef struct {
char display[32];
int display_len;
i64 accumulator;
i64 current;
int op; /* 0=none, 1=+, 2=-, 3=*, 4=/ */
int new_number;
} calc_state;
static void calc_update_display(calc_state *s) {
s->display_len = 0;
i64 val = s->current;
if (val < 0) { s->display[s->display_len++] = '-'; val = -val; }
if (val == 0) { s->display[s->display_len++] = '0'; }
else {
char tmp[20]; int tl = 0;
while (val > 0) { tmp[tl++] = '0' + (int)(val % 10); val /= 10; }
for (int i = tl-1; i >= 0; i--) s->display[s->display_len++] = tmp[i];
}
s->display[s->display_len] = 0;
}
static void *calc_on_create(app_ctx *ctx) {
calc_state *s = (calc_state *)0x6000000;
s->accumulator = 0; s->current = 0;
s->op = 0; s->new_number = 1;
calc_update_display(s);
const char *t = "Calculator";
for (int i = 0; t[i] && i < 47; i++) ctx->win->title[i] = t[i];
return s;
}
static void calc_on_event(void *state, app_ctx *ctx, desktop_event *ev) {
calc_state *s = (calc_state *)state;
if (ev->type != EV_KEY_DOWN) return;
char c = scan_to_ascii(ev->scancode, ev->shift);
if (c >= '0' && c <= '9') {
if (s->new_number) { s->current = 0; s->new_number = 0; }
s->current = s->current * 10 + (c - '0');
} else if (c == '+') { s->accumulator = s->current; s->op = 1; s->new_number = 1; }
else if (c == '-') { s->accumulator = s->current; s->op = 2; s->new_number = 1; }
else if (c == '*') { s->accumulator = s->current; s->op = 3; s->new_number = 1; }
else if (c == '/') { s->accumulator = s->current; s->op = 4; s->new_number = 1; }
else if (c == '=' || c == '\n') {
switch (s->op) {
case 1: s->current = s->accumulator + s->current; break;
case 2: s->current = s->accumulator - s->current; break;
case 3: s->current = s->accumulator * s->current; break;
case 4: s->current = (s->current != 0) ? s->accumulator / s->current : 0; break;
}
s->op = 0; s->new_number = 1;
} else if (c == 'c' || c == 'C') {
s->current = 0; s->accumulator = 0; s->op = 0; s->new_number = 1;
}
calc_update_display(s);
ctx->win->dirty = 1;
}
static void calc_on_draw(void *state, app_ctx *ctx) {
calc_state *s = (calc_state *)state;
int cx = ctx->client_x;
int cy = ctx->client_y;
int cw = ctx->client_w;
int ch = ctx->client_h;
du_fill_rect(&g_fb, cx, cy, cw, ch, DS_DARK_BG_SECONDARY);
/* 显示屏 */
int disp_h = 40;
du_fill_rounded_rect(&g_fb, cx + 8, cy + 8, cw - 16, disp_h,
DP_ABYSS_900, DU_RADIUS_MD);
du_rect_outline(&g_fb, cx + 8, cy + 8, cw - 16, disp_h,
DS_DARK_BORDER, DU_RADIUS_MD);
/* 数字右对齐 */
int text_x = cx + cw - 16 - s->display_len * (int)DU_ASCII_STEP - 8;
du_draw_string(&g_fb, s->display, text_x, cy + 16,
DS_DARK_TEXT_PRIMARY, DP_ABYSS_900, DU_ASCII_STEP);
/* 按钮网格 4×4 */
const char *btn_labels[] = {
"C", "+/-", "%", "/",
"7", "8", "9", "*",
"4", "5", "6", "-",
"1", "2", "3", "+",
"0", ".", "=", 0
};
int btn_w = (cw - 16 - 12) / 4;
int btn_h = 32;
int btn_y = cy + 8 + disp_h + 8;
for (int i = 0; i < 19; i++) {
int row = i / 4, col = i % 4;
int bx = cx + 8 + col * (btn_w + 3);
int by = btn_y + row * (btn_h + 3);
if (by + btn_h > cy + ch - 4) break;
u32 bg = (i < 4) ? DP_ABYSS_600 :
(i % 4 == 3) ? DS_DARK_ACCENT :
(i == 18) ? DP_SUCCESS :
DP_ABYSS_700;
du_fill_rounded_rect(&g_fb, bx, by, btn_w, btn_h, bg, DU_RADIUS_SM);
du_draw_string(&g_fb, btn_labels[i],
bx + (btn_w - 1*(int)DU_ASCII_STEP)/2,
by + (btn_h - (int)DU_ASCII_CELL_H)/2,
DS_DARK_TEXT_PRIMARY, bg, DU_ASCII_STEP);
}
}
static void calc_on_destroy(void *state) { (void)state; }
/* ============================================================
* 应用注册
* ============================================================ */
static void register_apps(void) {
/* 0: Terminal */
g_apps[0] = (app_descriptor){
"bash", "Terminal", 640, 440,
bash_on_create, bash_on_event, bash_on_draw, bash_on_destroy
};
g_app_count++;
/* 1: Editor */
g_apps[1] = (app_descriptor){
"editor", "Editor", 600, 450,
editor_on_create, editor_on_event, editor_on_draw, editor_on_destroy
};
g_app_count++;
/* 2: File Manager */
g_apps[2] = (app_descriptor){
"fileman", "Files", 500, 400,
0, 0, 0, 0 /* placeholder */
};
g_app_count++;
/* 3: Calculator */
g_apps[3] = (app_descriptor){
"calc", "Calculator", 280, 400,
calc_on_create, calc_on_event, calc_on_draw, calc_on_destroy
};
g_app_count++;
}
static void setup_desktop_icons(void) {
int start_x = (int)g_fb_w - DESKTOP_ICON_W - ICON_GUTTER_X;
int y = ICON_GUTTER_Y;
for (int i = 0; i < g_app_count; i++) {
if (!g_apps[i].on_create) continue;
g_icons[g_icon_count].app_id = i;
g_icons[g_icon_count].x = start_x;
g_icons[g_icon_count].y = y;
g_icons[g_icon_count].label = g_apps[i].display_name;
g_icon_count++;
y += DESKTOP_ICON_H;
}
}
/* ============================================================
* 应用启动
* ============================================================ */
static void launch_app(int app_id) {
if (app_id < 0 || app_id >= g_app_count) return;
app_descriptor *app = &g_apps[app_id];
if (!app->on_create) return;
/* 计算窗口位置(居中偏移) */
int wx = 80 + g_win_count * 30;
int wy = 40 + g_win_count * 30;
if (wx + (int)app->default_w > (int)g_fb_w - 20) wx = 80;
if (wy + (int)app->default_h > (int)g_fb_h - TASKBAR_H - 20) wy = 40;
desktop_window *win = win_create(app_id, app->display_name,
wx, wy, (int)app->default_w, (int)app->default_h);
if (!win) return;
/* 构建应用上下文 */
app_ctx ctx;
ctx.fb = &g_fb;
ctx.win = win;
ctx.client_x = win->x + BORDER_W;
ctx.client_y = win->y + TITLEBAR_H;
ctx.client_w = win->w - 2 * BORDER_W;
ctx.client_h = win->h - TITLEBAR_H - BORDER_W;
ctx.should_exit = 0;
ctx.block_read = g_block_read;
ctx.kernel_api = g_kernel_api;
win->app_state = app->on_create(&ctx);
win->dirty = 1;
slog("app launched");
}
/* ============================================================
* 全屏重绘
* ============================================================ */
static void redraw_all(void) {
/* 渐变背景 */
du_fill_bg_gradient(&g_fb, DP_ABYSS_900, DP_ABYSS_800);
/* 桌面图标 */
draw_desktop_icons();
/* 窗口(从底到顶) */
for (int i = 0; i < g_win_count; i++) {
desktop_window *w = &g_windows[i];
if (w->minimized || !w->visible) continue;
/* 窗口客户区背景 */
int cx = w->x + BORDER_W;
int cy = w->y + TITLEBAR_H;
int cw = w->w - 2 * BORDER_W;
int ch = w->h - TITLEBAR_H - BORDER_W;
/* 调用应用绘制 */
if (w->app_id >= 0 && w->app_id < g_app_count && g_apps[w->app_id].on_draw && w->app_state) {
app_ctx ctx;
ctx.fb = &g_fb;
ctx.win = w;
ctx.client_x = cx; ctx.client_y = cy;
ctx.client_w = cw; ctx.client_h = ch;
ctx.should_exit = 0;
ctx.block_read = g_block_read;
ctx.kernel_api = g_kernel_api;
g_apps[w->app_id].on_draw(w->app_state, &ctx);
}
/* 窗口装饰(在应用内容之上) */
draw_window_frame(w);
}
/* 任务栏 */
draw_taskbar();
/* 鼠标光标 */
cursor_save_bg(g_mouse_x, g_mouse_y);
cursor_draw(g_mouse_x, g_mouse_y);
}
/* ============================================================
* 主循环
* ============================================================ */
__attribute__((visibility("default")))
void dsk_entry(const dsk_boot_context *ctx) {
__asm__ volatile("cli");
slog("boot");
if (!ctx || ctx->magic != 0x44534B31424F4F54ULL) {
slog("bad context");
for(;;) __asm__("hlt");
}
g_fb_addr = ctx->framebuffer_address;
g_fb_w = ctx->framebuffer_width;
g_fb_h = ctx->framebuffer_height;
g_fb_pitch = ctx->framebuffer_pitch;
g_kernel_api = (const void *)ctx->dkm_kernel_api;
g_block_read = (desktop_block_read_fn)(*(u64 *)(ctx->dkm_kernel_api + 0xA8));
du_context_init(&g_fb, g_fb_addr, g_fb_w, g_fb_h, g_fb_pitch);
/* 初始化 PS/2 鼠标 */
ps2_mouse_init();
/* 注册应用 */
register_apps();
setup_desktop_icons();
/* 初始绘制 */
redraw_all();
slog("desktop ready");
/* 主事件循环 */
for (;;) {
int need_redraw = 0;
g_mouse_has_pkt = 0;
/* 轮询 PS/2 鼠标 */
if (ps2_mouse_poll()) {
need_redraw = 1;
/* 拖拽处理 */
if (g_dragging && g_drag_win >= 0) {
desktop_window *w = win_find(g_drag_win);
if (w) {
w->x = g_mouse_x - g_drag_off_x;
w->y = g_mouse_y - g_drag_off_y;
}
}
/* 鼠标点击事件 */
if (g_mouse_btn) {
/* 检查任务栏点击 */
int tb_y = (int)g_fb_h - TASKBAR_H;
if (g_mouse_y >= tb_y) {
/* 快速启动图标 */
int qx = 64;
for (int i = 0; i < g_app_count && i < 4; i++) {
int icon_x = qx + i * 40;
if (g_mouse_x >= icon_x && g_mouse_x < icon_x + 40) {
launch_app(i);
need_redraw = 1;
break;
}
}
} else {
/* 窗口命中测试 */
desktop_window *hit = win_find_at(g_mouse_x, g_mouse_y);
if (hit) {
win_bring_to_front(hit);
/* 取消其他焦点 */
for (int i = 0; i < g_win_count; i++)
g_windows[i].focused = (g_windows[i].id == hit->id);
g_focused_win = hit->id;
/* 检查关闭按钮 */
if (win_hit_close_btn(hit, g_mouse_x, g_mouse_y)) {
win_destroy(hit);
need_redraw = 1;
}
/* 检查标题栏拖拽 */
else if (win_hit_titlebar(hit, g_mouse_x, g_mouse_y)) {
g_dragging = 1;
g_drag_win = hit->id;
g_drag_off_x = g_mouse_x - hit->x;
g_drag_off_y = g_mouse_y - hit->y;
}
/* 传递鼠标事件给应用 */
else if (hit->app_id >= 0 && g_apps[hit->app_id].on_event && hit->app_state) {
desktop_event dev;
dev.type = EV_MOUSE_DOWN;
dev.mx = g_mouse_x; dev.my = g_mouse_y;
dev.button = 0;
app_ctx actx;
actx.fb = &g_fb; actx.win = hit;
actx.client_x = hit->x + BORDER_W;
actx.client_y = hit->y + TITLEBAR_H;
actx.client_w = hit->w - 2*BORDER_W;
actx.client_h = hit->h - TITLEBAR_H - BORDER_W;
actx.should_exit = 0;
actx.block_read = g_block_read;
actx.kernel_api = g_kernel_api;
g_apps[hit->app_id].on_event(hit->app_state, &actx, &dev);
}
} else {
/* 桌面图标点击 */
for (int i = 0; i < g_icon_count; i++) {
int ix = g_icons[i].x, iy = g_icons[i].y;
if (g_mouse_x >= ix && g_mouse_x < ix + 32 &&
g_mouse_y >= iy && g_mouse_y < iy + 32) {
launch_app(g_icons[i].app_id);
need_redraw = 1;
break;
}
}
}
}
} else {
if (g_dragging) {
g_dragging = 0;
g_drag_win = -1;
}
}
}
/* 轮询 PS/2 键盘 */
{
u8 st = inb(0x64);
if ((st & 1) && !(st & 0x20)) {
u8 data = inb(0x60);
u8 sc = data;
if (sc == 0xE0) { g_e0 = 1; goto next; }
if (sc == 0x2A || sc == 0x36) { g_shift = 1; goto next; }
if (sc == 0xAA || sc == 0xB6) { g_shift = 0; goto next; }
if (sc & 0x80) { g_e0 = 0; goto next; }
/* 发送键盘事件到焦点窗口 */
if (g_focused_win >= 0) {
desktop_window *fw = win_find(g_focused_win);
if (fw && fw->app_id >= 0 && g_apps[fw->app_id].on_event && fw->app_state) {
desktop_event dev;
dev.type = EV_KEY_DOWN;
dev.mx = g_mouse_x; dev.my = g_mouse_y;
dev.button = 0;
dev.scancode = sc;
dev.shift = g_shift;
dev.ctrl = g_ctrl;
dev.alt = g_alt;
app_ctx actx;
actx.fb = &g_fb; actx.win = fw;
actx.client_x = fw->x + BORDER_W;
actx.client_y = fw->y + TITLEBAR_H;
actx.client_w = fw->w - 2*BORDER_W;
actx.client_h = fw->h - TITLEBAR_H - BORDER_W;
actx.should_exit = 0;
actx.block_read = g_block_read;
actx.kernel_api = g_kernel_api;
g_apps[fw->app_id].on_event(fw->app_state, &actx, &dev);
need_redraw = 1;
}
}
g_e0 = 0;
}
}
next:
if (need_redraw) {
cursor_restore_bg();
redraw_all();
}
__asm__("pause");
}
}
+20 -20
View File
@@ -373,31 +373,31 @@ static void dsk_spinner_and_check(u32 *fb_arg, i64 cx, i64 cy, i64 r, i64 thk,
if(fe_entry&&frame>150){logl("[DSK] jumping to FirstInit"); /* was 600 */
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");
+126 -163
View File
@@ -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<ASCII_H; r++) {
i64 yy = y + r;
if (yy < 0 || (u64)yy >= fb_h) continue;
u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p);
for (i64 c=0; c<ASCII_W; c++) {
u32 a = g[r * ASCII_W + c];
if (a == 0) continue;
i64 xx = x + c;
if (xx < 0 || (u64)xx >= fb_w) continue;
line[(u64)xx] = (a == 255) ? fg : blend(bg, fg, a);
}
}
}
static void fill_rect(u32 *fb, i64 x, i64 y, i64 w, i64 h, u32 color) {
for (i64 r=0; r<h; r++) {
i64 yy = y + r;
if (yy < 0 || (u64)yy >= fb_h) continue;
u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p);
for (i64 c=0; c<w; c++) {
i64 xx = x + c;
if (xx < 0 || (u64)xx >= fb_w) continue;
line[(u64)xx] = color;
}
}
}
/* ---- 终端逻辑 ---- */
static int term_start_x, term_start_y; /* 像素坐标 */
/* ---- 终端像素坐标 ---- */
static 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);
+25
View File
@@ -0,0 +1,25 @@
CC := clang
LD := ld.lld
ROOT := ../../..
OUT := $(ROOT)/SYSTEM/system/deshab64/tools/bash.elf
CFLAGS := -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fpie -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -I.. -I../../UTSM/include -O2
LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld
C_SOURCES := main.c
OBJECTS := $(C_SOURCES:.c=.o)
.PHONY: all clean
all: $(OUT)
$(OUT): $(OBJECTS) linker.ld
@if not exist "$(ROOT)\SYSTEM\system\deshab64\tools" mkdir "$(ROOT)\SYSTEM\system\deshab64\tools"
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }"
+16
View File
@@ -0,0 +1,16 @@
ENTRY(dsk_entry)
PHDRS
{
text PT_LOAD FLAGS(7);
rodata PT_LOAD FLAGS(4);
data PT_LOAD FLAGS(7);
}
SECTIONS
{
. = 0x1000;
.text : ALIGN(4K) { *(.text*) } :text
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
.data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data
}
+456
View File
@@ -0,0 +1,456 @@
/* Deshab Bash — 全屏终端应用
* PS/2 键盘输入,命令行编辑,内置命令。
* Esc 键退出返回 DSK。
*/
#include "../../firstInit/ascii_bitmaps.c"
#include "../desktop_app.h"
/* ---- 终端参数 ---- */
#define TERM_COLS 120
#define TERM_ROWS 40
#define TERM_MAX_CHARS (TERM_COLS * TERM_ROWS)
#define CHAR_STEP 12
#define CHAR_H 18
#define PROMPT_STR "deshab# "
static da_app_context g_ac;
/* 终端缓冲区 */
static u8 term_ch[TERM_MAX_CHARS];
static u32 term_fg[TERM_MAX_CHARS];
static int term_w = TERM_COLS;
static int term_h = TERM_ROWS;
static int cur_col = 0;
static int cur_row = 0;
/* 输入缓冲 */
static char input_buf[256];
static int input_len = 0;
static int input_cursor = 0;
/* 命令历史 */
#define HIST_SIZE 16
static char history[HIST_SIZE][256];
static int hist_count = 0;
static int hist_idx = 0;
/* 坐标 */
static int term_x, term_y;
static int content_h;
static void term_init_layout(void) {
term_x = 8;
term_y = DA_TITLEBAR_H + 4;
int avail_w = (int)g_ac.fb_w - 16;
int avail_h = (int)g_ac.fb_h - term_y - DA_STATUSBAR_H - 8;
term_w = avail_w / CHAR_STEP;
term_h = avail_h / CHAR_H;
if (term_w < 10) term_w = 10;
if (term_h < 5) term_h = 5;
if (term_w > TERM_COLS) term_w = TERM_COLS;
if (term_h > TERM_ROWS) term_h = TERM_ROWS;
content_h = term_h * CHAR_H;
}
static void term_clear(void) {
for (int i = 0; i < TERM_MAX_CHARS; i++) {
term_ch[i] = ' ';
term_fg[i] = DA_TEXT_PRIMARY;
}
cur_col = 0; cur_row = 0;
}
static void term_scroll(void) {
for (int r = 1; r < term_h; r++) {
for (int c = 0; c < term_w; c++) {
term_ch[(r-1)*TERM_COLS+c] = term_ch[r*TERM_COLS+c];
term_fg[(r-1)*TERM_COLS+c] = term_fg[r*TERM_COLS+c];
}
}
for (int c = 0; c < term_w; c++) {
term_ch[(term_h-1)*TERM_COLS+c] = ' ';
term_fg[(term_h-1)*TERM_COLS+c] = DA_TEXT_PRIMARY;
}
cur_row = term_h - 1; cur_col = 0;
}
static void term_newline(void) {
cur_col = 0; cur_row++;
if (cur_row >= term_h) term_scroll();
}
static void term_putc_color(char c, u32 color) {
if (c == '\n') { term_newline(); return; }
if (c == '\r') { cur_col = 0; return; }
if (c == '\t') { int sp = 4 - (cur_col % 4); for (int i = 0; i < sp; i++) term_putc_color(' ', color); return; }
if (c < ' ' || c > '~') c = '?';
if (cur_col >= term_w) term_newline();
int idx = cur_row * TERM_COLS + cur_col;
term_ch[idx] = (u8)c; term_fg[idx] = color;
cur_col++;
}
static void term_putc(char c) { term_putc_color(c, DA_TEXT_PRIMARY); }
static void term_puts_color(const char *s, u32 color) { while (*s) term_putc_color(*s++, color); }
static void term_puts(const char *s) { term_puts_color(s, DA_TEXT_PRIMARY); }
/* ---- 绘制 ---- */
static void redraw_all(void) {
da_fill_bg(&g_ac, DA_BG_PRIMARY);
da_draw_titlebar(&g_ac, "Deshab Bash", (i64)g_ac.fb_w);
/* 终端字符 */
for (int r = 0; r < term_h; r++) {
for (int c = 0; c < term_w; c++) {
int idx = r * TERM_COLS + c;
if (term_ch[idx] == ' ') continue;
da_draw_char(&g_ac, term_ch[idx],
term_x + c * CHAR_STEP, term_y + r * CHAR_H,
term_fg[idx], DA_BG_PRIMARY);
}
}
/* 光标 */
if (cur_col < term_w && cur_row < term_h) {
da_fill_rect(&g_ac, term_x + cur_col * CHAR_STEP,
term_y + cur_row * CHAR_H + CHAR_H - 3, 11, 2, DA_ACCENT);
}
/* 状态栏 */
char status[64];
int p = 0;
const char *s1 = "/ root@deshab ";
while (*s1) status[p++] = *s1++;
u8 hh, mm; da_rtc_time(&hh, &mm);
status[p++] = '0' + hh/10; status[p++] = '0' + hh%10;
status[p++] = ':'; status[p++] = '0' + mm/10; status[p++] = '0' + mm%10;
status[p] = 0;
da_draw_statusbar(&g_ac, status, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
}
/* ---- 内置命令 ---- */
static int str_eq(const char *a, const char *b) {
while (*a && *b) { if (*a != *b) return 0; a++; b++; }
return *a == *b;
}
static void cmd_help(void) {
term_puts_color("Deshab Bash - 内置命令:\n", DA_PROMPT_COLOR);
term_puts(" help 显示此帮助\n");
term_puts(" clear 清屏\n");
term_puts(" echo <text> 显示文本\n");
term_puts(" version 显示系统版本\n");
term_puts(" ver 同 version\n");
term_puts(" uname 显示系统名称\n");
term_puts(" date 显示当前日期时间\n");
term_puts(" about 关于 Deshab\n");
term_puts(" reboot 重启系统\n");
term_puts(" halt 关机\n");
term_puts(" pwd 显示当前目录\n");
term_puts(" whoami 显示当前用户\n");
term_puts(" id 显示用户ID\n");
term_puts(" ls 列出根目录文件\n");
term_puts(" cat <file> 显示文件内容\n");
}
static void cmd_version(void) {
term_puts_color("Deshab OS v0.1.0\n", DA_SUCCESS);
term_puts(" Kernel: DSK (Deshab System Kernel)\n");
term_puts(" Architecture: x86_64\n");
term_puts(" ABI: DKM v1\n");
term_puts(" Bootloader: Limine\n");
}
static void cmd_uname(void) { term_puts("Deshab\n"); }
static void cmd_about(void) {
term_puts_color("=== Deshab OS ===\n", DA_PROMPT_COLOR);
term_puts("单地址空间 Ring0 内核实验系统\n");
term_puts("架构: UTSM + DSK + DKM\n");
term_puts_color("Built with Clang + lld\n", DA_TEXT_DIM);
}
static void cmd_clear(void) { term_clear(); }
static void cmd_reboot(void) {
term_puts_color("Rebooting...\n", DA_PROMPT_COLOR);
redraw_all();
outb(0x64, 0xFE);
for(;;) __asm__("hlt");
}
static void cmd_halt(void) {
term_puts_color("System halted.\n", DA_PROMPT_COLOR);
redraw_all();
for(;;) __asm__("hlt");
}
static void cmd_echo(const char *args) {
if (args && *args) term_puts_color(args, DA_TEXT_PRIMARY);
term_putc('\n');
}
static void cmd_date(void) {
u8 year, month, day, hour, minute, second;
outb(0x70, 9); year = inb(0x71);
outb(0x70, 8); month = inb(0x71);
outb(0x70, 7); day = inb(0x71);
outb(0x70, 4); hour = inb(0x71);
outb(0x70, 2); minute = inb(0x71);
outb(0x70, 0); second = inb(0x71);
year = (u8)((year>>4)*10+(year&0xF));
month = (u8)((month>>4)*10+(month&0xF));
day = (u8)((day>>4)*10+(day&0xF));
hour = (u8)((hour>>4)*10+(hour&0xF));
minute = (u8)((minute>>4)*10+(minute&0xF));
second = (u8)((second>>4)*10+(second&0xF));
char buf[32]; int p = 0;
buf[p++]='2'; buf[p++]='0'; buf[p++]='0'+year/10; buf[p++]='0'+year%10; buf[p++]='-';
buf[p++]='0'+month/10; buf[p++]='0'+month%10; buf[p++]='-';
buf[p++]='0'+day/10; buf[p++]='0'+day%10; buf[p++]=' ';
buf[p++]='0'+hour/10; buf[p++]='0'+hour%10; buf[p++]=':';
buf[p++]='0'+minute/10; buf[p++]='0'+minute%10; buf[p++]=':';
buf[p++]='0'+second/10; buf[p++]='0'+second%10; buf[p]=0;
term_puts(buf); term_putc('\n');
}
static void cmd_ls(void) {
if (!g_ac.block_read) {
term_puts_color("ls: 无块设备可用\n", DA_ERROR);
return;
}
/* 读取 FAT32 根目录 — 简化版:读取 BPB 获取根目录区 */
u8 bpb[512];
if (g_ac.block_read(0, 0, 1, bpb) != 0) {
term_puts_color("ls: 读取 BPB 失败\n", DA_ERROR);
return;
}
/* 检查 FAT32 签名 */
if (bpb[510] != 0x55 || bpb[511] != 0xAA) {
term_puts_color("ls: 无有效 FAT32 分区\n", DA_ERROR);
return;
}
u32 root_cluster = *(u32 *)(bpb + 44);
u16 bytes_per_sector = *(u16 *)(bpb + 11);
u8 sectors_per_cluster = bpb[13];
u32 fat_start = *(u16 *)(bpb + 14);
u32 data_start = fat_start + (*(u16 *)(bpb + 22)) * 2;
(void)bytes_per_sector;
(void)sectors_per_cluster;
(void)data_start;
/* 读取根目录簇 — 简化:只读取第一个簇 */
u8 dir_buf[8192];
u32 cluster = root_cluster;
u32 lba = data_start + (cluster - 2) * sectors_per_cluster;
if (g_ac.block_read(0, lba, sectors_per_cluster, dir_buf) != 0) {
term_puts_color("ls: 读取根目录失败\n", DA_ERROR);
return;
}
/* 解析目录项 */
int found = 0;
for (int i = 0; i < (int)(sectors_per_cluster * 512); i += 32) {
u8 first = dir_buf[i];
if (first == 0x00) break;
if (first == 0xE5) continue;
if (dir_buf[i+11] & 0x08) continue; /* 卷标 */
if (dir_buf[i+11] & 0x10) {
/* 目录 */
term_puts_color("[DIR] ", DA_ACCENT);
} else {
term_puts_color(" ", DA_TEXT_PRIMARY);
}
/* 文件名(8.3) */
for (int j = 0; j < 8; j++) {
if (dir_buf[i+j] == ' ') break;
term_putc((char)dir_buf[i+j]);
}
if (dir_buf[i+8] != ' ') {
term_putc('.');
for (int j = 8; j < 11; j++) {
if (dir_buf[i+j] == ' ') break;
term_putc((char)dir_buf[i+j]);
}
}
term_putc('\n');
found++;
}
if (found == 0) term_puts_color("(empty)\n", DA_TEXT_DIM);
}
static void cmd_cat(const char *args) {
if (!args || !*args) {
term_puts_color("cat: 需要文件名参数\n", DA_ERROR);
return;
}
if (!g_ac.block_read) {
term_puts_color("cat: 无块设备可用\n", DA_ERROR);
return;
}
term_puts_color("cat: 文件读取暂未完整实现\n", DA_WARNING);
}
static void execute_command(const char *cmd) {
while (*cmd == ' ') cmd++;
if (*cmd == 0) return;
const char *args = cmd;
while (*args && *args != ' ') args++;
char name[32];
int nlen = (int)(args - cmd);
if (nlen >= 32) nlen = 31;
for (int i = 0; i < nlen; i++) name[i] = cmd[i];
name[nlen] = 0;
while (*args == ' ') args++;
if (str_eq(name,"help")||str_eq(name,"?")) cmd_help();
else if (str_eq(name,"version")||str_eq(name,"ver")) cmd_version();
else if (str_eq(name,"uname")) cmd_uname();
else if (str_eq(name,"about")) cmd_about();
else if (str_eq(name,"clear")||str_eq(name,"cls")) cmd_clear();
else if (str_eq(name,"reboot")) cmd_reboot();
else if (str_eq(name,"halt")||str_eq(name,"shutdown")) cmd_halt();
else if (str_eq(name,"echo")) cmd_echo(args);
else if (str_eq(name,"date")||str_eq(name,"time")) cmd_date();
else if (str_eq(name,"ls")||str_eq(name,"dir")) cmd_ls();
else if (str_eq(name,"cat")) cmd_cat(args);
else if (str_eq(name,"pwd")) { term_puts("/\n"); }
else if (str_eq(name,"whoami")) { term_puts("root\n"); }
else if (str_eq(name,"id")) { term_puts("uid=0(root) gid=0(root)\n"); }
else {
term_puts_color("未知命令: ", DA_ERROR);
term_puts_color(name, DA_ERROR);
term_putc('\n');
term_puts_color("输入 'help' 查看可用命令\n", DA_TEXT_DIM);
}
}
static void add_history(const char *cmd) {
if (hist_count < HIST_SIZE) {
int i;
for (i = 0; cmd[i] && i < 255; i++) history[hist_count][i] = cmd[i];
history[hist_count][i] = 0;
hist_count++;
}
hist_idx = hist_count;
}
/* ---- 输入行重绘 ---- */
static void redraw_input_line(void) {
int plen = 0;
while (PROMPT_STR[plen]) plen++;
i64 y = term_y + (i64)cur_row * CHAR_H;
da_fill_rect(&g_ac, term_x, y, (i64)term_w * CHAR_STEP, CHAR_H, DA_BG_PRIMARY);
for (int i = 0; i < plen; i++)
da_draw_char(&g_ac, PROMPT_STR[i], term_x + i * CHAR_STEP, y, DA_PROMPT_COLOR, DA_BG_PRIMARY);
for (int i = 0; i < input_len; i++)
da_draw_char(&g_ac, input_buf[i], term_x + (plen + i) * CHAR_STEP, y, DA_TEXT_PRIMARY, DA_BG_PRIMARY);
cur_col = plen + input_cursor;
/* 光标 */
da_fill_rect(&g_ac, term_x + cur_col * CHAR_STEP, y + CHAR_H - 3, 11, 2, DA_ACCENT);
}
/* ---- 主入口 ---- */
__attribute__((visibility("default")))
void dsk_entry(const da_boot_context *ctx) {
__asm__ volatile("cli");
da_slog("bash", "boot");
if (!ctx || ctx->magic != DA_BOOT_MAGIC) {
da_slog("bash", "bad context");
for(;;) __asm__("hlt");
}
da_init(&g_ac, ctx);
term_init_layout();
term_clear();
/* 启动 banner */
term_puts_color("=== Deshab Bash v0.1 ===\n", DA_ACCENT);
term_puts_color("欢迎使用 Deshab 终端\n", DA_SUCCESS);
term_puts_color("输入 'help' 查看可用命令 | Esc 退出\n", DA_TEXT_DIM);
term_putc('\n');
redraw_all();
int shift = 0, e0 = 0;
for (;;) {
/* 绘制提示符 */
term_puts_color(PROMPT_STR, DA_PROMPT_COLOR);
input_len = 0; input_cursor = 0; input_buf[0] = 0;
int plen = 0; while (PROMPT_STR[plen]) plen++;
cur_col = plen;
redraw_all();
int cmd_done = 0;
while (!cmd_done) {
u8 st = inb(0x64);
if (!(st & 1)) { __asm__("pause"); continue; }
u8 data = inb(0x60);
if (st & 0x20) continue;
u8 sc = data;
if (sc == 0xE0) { e0 = 1; continue; }
if (sc == 0x2A || sc == 0x36) { shift = 1; continue; }
if (sc == 0xAA || sc == 0xB6) { shift = 0; continue; }
if (sc & 0x80) { e0 = 0; continue; }
/* Esc 退出 */
if (sc == 0x01 && !e0) {
da_slog("bash", "exit");
return;
}
if (e0) {
if (sc == 0x4B) { if (input_cursor > 0) input_cursor--; }
else if (sc == 0x4D) { if (input_cursor < input_len) input_cursor++; }
else if (sc == 0x47) { input_cursor = 0; }
else if (sc == 0x4F) { input_cursor = input_len; }
else if (sc == 0x48) { /* 上箭头 - 历史 */
if (hist_idx > 0) {
hist_idx--;
int i; for (i = 0; history[hist_idx][i] && i < 255; i++) input_buf[i] = history[hist_idx][i];
input_len = i; input_cursor = i; input_buf[i] = 0;
}
}
else if (sc == 0x50) { /* 下箭头 - 历史 */
if (hist_idx < hist_count - 1) {
hist_idx++;
int i; for (i = 0; history[hist_idx][i] && i < 255; i++) input_buf[i] = history[hist_idx][i];
input_len = i; input_cursor = i; input_buf[i] = 0;
} else if (hist_idx < hist_count) {
hist_idx = hist_count;
input_len = 0; input_cursor = 0; input_buf[0] = 0;
}
}
e0 = 0;
redraw_input_line();
continue;
}
if (sc == 0x1C) { /* Enter */
input_buf[input_len] = 0;
term_putc('\n');
if (input_len > 0) add_history(input_buf);
execute_command(input_buf);
cmd_done = 1;
continue;
}
if (sc == 0x0E) { /* Backspace */
if (input_cursor > 0) {
for (int i = input_cursor - 1; i < input_len - 1; i++)
input_buf[i] = input_buf[i+1];
input_len--; input_cursor--;
input_buf[input_len] = 0;
}
redraw_input_line();
continue;
}
char c = da_scan_to_ascii(sc, shift);
if (c && c >= 32 && c <= 126 && input_len < 255) {
for (int i = input_len; i > input_cursor; i--)
input_buf[i] = input_buf[i-1];
input_buf[input_cursor++] = c;
input_len++;
redraw_input_line();
}
}
redraw_all();
}
}
+25
View File
@@ -0,0 +1,25 @@
CC := clang
LD := ld.lld
ROOT := ../../..
OUT := $(ROOT)/SYSTEM/system/deshab64/tools/browser.elf
CFLAGS := -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fpie -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -I.. -I../../UTSM/include -O2
LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld
C_SOURCES := main.c
OBJECTS := $(C_SOURCES:.c=.o)
.PHONY: all clean
all: $(OUT)
$(OUT): $(OBJECTS) linker.ld
@if not exist "$(ROOT)\SYSTEM\system\deshab64\tools" mkdir "$(ROOT)\SYSTEM\system\deshab64\tools"
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }"
+16
View File
@@ -0,0 +1,16 @@
ENTRY(dsk_entry)
PHDRS
{
text PT_LOAD FLAGS(7);
rodata PT_LOAD FLAGS(4);
data PT_LOAD FLAGS(7);
}
SECTIONS
{
. = 0x1000;
.text : ALIGN(4K) { *(.text*) } :text
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
.data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data
}
+128
View File
@@ -0,0 +1,128 @@
/* Deshab Browser — 全屏浏览器 (Stub)
* 地址栏 + 导航按钮 + 内容渲染区域。
* 当前显示 stub 提示,Esc 退出。
*/
#include "../../firstInit/ascii_bitmaps.c"
#include "../desktop_app.h"
#define CHAR_STEP 12
#define CHAR_H 18
static da_app_context g_ac;
static da_cursor g_cursor;
static da_mouse g_mouse;
static void redraw_all(void) {
da_fill_bg(&g_ac, DA_BG_PRIMARY);
/* 标题栏 */
da_draw_titlebar(&g_ac, "Browser", (i64)g_ac.fb_w);
/* 地址栏 */
int bar_y = DA_TITLEBAR_H + 6;
int bar_h = 28;
int bar_x = 12;
int bar_w = (int)g_ac.fb_w - 24;
da_fill_rounded_rect(&g_ac, bar_x, bar_y, bar_w, bar_h, DA_BG_TERTIARY, 6);
da_rect_outline(&g_ac, bar_x, bar_y, bar_w, bar_h, DA_BORDER, 6);
/* 导航按钮 */
int btn_w = 32;
da_fill_rounded_rect(&g_ac, bar_x + 4, bar_y + 4, btn_w, bar_h - 8, DA_BG_SECONDARY, 4);
da_draw_string(&g_ac, "<", bar_x + 14, bar_y + 5, DA_TEXT_PRIMARY, DA_BG_SECONDARY, CHAR_STEP);
da_fill_rounded_rect(&g_ac, bar_x + 4 + btn_w + 4, bar_y + 4, btn_w, bar_h - 8, DA_BG_SECONDARY, 4);
da_draw_string(&g_ac, ">", bar_x + 4 + btn_w + 4 + 12, bar_y + 5, DA_TEXT_PRIMARY, DA_BG_SECONDARY, CHAR_STEP);
da_fill_rounded_rect(&g_ac, bar_x + 4 + (btn_w + 4) * 2, bar_y + 4, btn_w, bar_h - 8, DA_BG_SECONDARY, 4);
da_draw_string(&g_ac, "R", bar_x + 4 + (btn_w + 4) * 2 + 12, bar_y + 5, DA_TEXT_PRIMARY, DA_BG_SECONDARY, CHAR_STEP);
/* URL 文本 */
int url_x = bar_x + (btn_w + 4) * 3 + 8;
da_draw_string(&g_ac, "deshab://home", url_x, bar_y + 5, DA_TEXT_DIM, DA_BG_TERTIARY, CHAR_STEP);
/* 内容区域 */
int content_y = bar_y + bar_h + 12;
int content_x = 16;
int content_w = (int)g_ac.fb_w - 32;
/* Stub 页面 */
da_fill_rounded_rect(&g_ac, content_x, content_y, content_w, 200, DA_BG_SECONDARY, 8);
da_rect_outline(&g_ac, content_x, content_y, content_w, 200, DA_BORDER, 8);
/* 居中图标 */
int icon_cx = content_x + content_w / 2 - 32;
int icon_cy = content_y + 24;
da_fill_rounded_rect(&g_ac, icon_cx, icon_cy, 64, 48, DA_BG_TERTIARY, 8);
/* 地球图标简化:圆圈 */
int cx = icon_cx + 32, cy = icon_cy + 24, r = 16;
for (int dy = -r; dy <= r; dy++)
for (int dx = -r; dx <= r; dx++)
if (dx*dx+dy*dy <= r*r && dx*dx+dy*dy > (r-1)*(r-1))
da_pixel(&g_ac, cx+dx, cy+dy, DA_ACCENT);
/* 横线 */
for (int dx = -r; dx <= r; dx++)
if (dx*dx + 0 <= r*r)
da_pixel(&g_ac, cx+dx, cy, DA_ACCENT);
/* 竖线 */
for (int dy = -r; dy <= r; dy++)
if (0 + dy*dy <= r*r)
da_pixel(&g_ac, cx, cy+dy, DA_ACCENT);
/* 提示文字 */
da_draw_string(&g_ac, "Deshab Browser",
content_x + content_w / 2 - 7 * CHAR_STEP, icon_cy + 60,
DA_TEXT_PRIMARY, DA_BG_SECONDARY, CHAR_STEP);
da_draw_string(&g_ac, "HTTP + HTML rendering required",
content_x + content_w / 2 - 15 * CHAR_STEP, icon_cy + 82,
DA_TEXT_DIM, DA_BG_SECONDARY, CHAR_STEP);
da_draw_string(&g_ac, "Press Esc to exit",
content_x + content_w / 2 - 9 * CHAR_STEP, icon_cy + 104,
DA_TEXT_DIM, DA_BG_SECONDARY, CHAR_STEP);
/* 状态栏 */
da_draw_statusbar(&g_ac, "Browser | Stub - No network stack", (i64)g_ac.fb_w, (i64)g_ac.fb_h);
/* 鼠标光标 */
da_cursor_save(&g_ac, &g_cursor);
da_cursor_draw(&g_ac, &g_cursor, DA_CURSOR_COLOR);
}
__attribute__((visibility("default")))
void dsk_entry(const da_boot_context *ctx) {
__asm__ volatile("cli");
da_slog("browser", "boot");
if (!ctx || ctx->magic != DA_BOOT_MAGIC) {
da_slog("browser", "bad context");
for(;;) __asm__("hlt");
}
da_init(&g_ac, ctx);
da_cursor_init(&g_cursor, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
da_mouse_init();
redraw_all();
for (;;) {
int need_redraw = 0;
/* 鼠标轮询 */
if (da_mouse_poll(&g_mouse, &g_cursor, (i64)g_ac.fb_w, (i64)g_ac.fb_h))
need_redraw = 1;
/* 键盘轮询 */
u8 st = inb(0x64);
if ((st & 1) && !(st & 0x20)) {
u8 data = inb(0x60);
if (!(data & 0x80) && data == 0x01) {
da_slog("browser", "exit");
return;
}
}
if (need_redraw) {
da_cursor_restore(&g_ac, &g_cursor);
redraw_all();
}
__asm__("pause");
}
}
+25
View File
@@ -0,0 +1,25 @@
CC := clang
LD := ld.lld
ROOT := ../../..
OUT := $(ROOT)/SYSTEM/system/deshab64/tools/curl.elf
CFLAGS := -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fpie -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -I.. -I../../UTSM/include -O2
LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld
C_SOURCES := main.c
OBJECTS := $(C_SOURCES:.c=.o)
.PHONY: all clean
all: $(OUT)
$(OUT): $(OBJECTS) linker.ld
@if not exist "$(ROOT)\SYSTEM\system\deshab64\tools" mkdir "$(ROOT)\SYSTEM\system\deshab64\tools"
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }"
+16
View File
@@ -0,0 +1,16 @@
ENTRY(dsk_entry)
PHDRS
{
text PT_LOAD FLAGS(7);
rodata PT_LOAD FLAGS(4);
data PT_LOAD FLAGS(7);
}
SECTIONS
{
. = 0x1000;
.text : ALIGN(4K) { *(.text*) } :text
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
.data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data
}
+324
View File
@@ -0,0 +1,324 @@
/* Deshab curl — 终端风格 HTTP 客户端 stub
* 命令行输入 URL,显示 HTTP GET 请求结果。
* 当前为 stub,提示需要 TCP/IP 栈支持。
* Esc 键退出返回 DSK。
*/
#include "../../firstInit/ascii_bitmaps.c"
#include "../desktop_app.h"
/* ---- 终端参数 ---- */
#define TERM_COLS 120
#define TERM_ROWS 40
#define TERM_MAX_CHARS (TERM_COLS * TERM_ROWS)
#define CHAR_STEP 12
#define CHAR_H 18
#define PROMPT_STR "curl> "
static da_app_context g_ac;
/* 终端缓冲区 */
static u8 term_ch[TERM_MAX_CHARS];
static u32 term_fg[TERM_MAX_CHARS];
static int term_w = TERM_COLS;
static int term_h = TERM_ROWS;
static int cur_col = 0;
static int cur_row = 0;
/* 输入缓冲 */
static char input_buf[256];
static int input_len = 0;
static int input_cursor = 0;
/* 命令历史 */
#define HIST_SIZE 16
static char history[HIST_SIZE][256];
static int hist_count = 0;
static int hist_idx = 0;
/* 坐标 */
static int term_x, term_y;
static void term_init_layout(void) {
term_x = 8;
term_y = DA_TITLEBAR_H + 4;
int avail_w = (int)g_ac.fb_w - 16;
int avail_h = (int)g_ac.fb_h - term_y - DA_STATUSBAR_H - 8;
term_w = avail_w / CHAR_STEP;
term_h = avail_h / CHAR_H;
if (term_w < 10) term_w = 10;
if (term_h < 5) term_h = 5;
if (term_w > TERM_COLS) term_w = TERM_COLS;
if (term_h > TERM_ROWS) term_h = TERM_ROWS;
}
static void term_clear(void) {
for (int i = 0; i < TERM_MAX_CHARS; i++) {
term_ch[i] = ' ';
term_fg[i] = DA_TEXT_PRIMARY;
}
cur_col = 0; cur_row = 0;
}
static void term_scroll(void) {
for (int r = 1; r < term_h; r++) {
for (int c = 0; c < term_w; c++) {
term_ch[(r-1)*TERM_COLS+c] = term_ch[r*TERM_COLS+c];
term_fg[(r-1)*TERM_COLS+c] = term_fg[r*TERM_COLS+c];
}
}
for (int c = 0; c < term_w; c++) {
term_ch[(term_h-1)*TERM_COLS+c] = ' ';
term_fg[(term_h-1)*TERM_COLS+c] = DA_TEXT_PRIMARY;
}
cur_row = term_h - 1; cur_col = 0;
}
static void term_newline(void) {
cur_col = 0; cur_row++;
if (cur_row >= term_h) term_scroll();
}
static void term_putc_color(char c, u32 color) {
if (c == '\n') { term_newline(); return; }
if (c == '\r') { cur_col = 0; return; }
if (c == '\t') { int sp = 4 - (cur_col % 4); for (int i = 0; i < sp; i++) term_putc_color(' ', color); return; }
if (c < ' ' || c > '~') c = '?';
if (cur_col >= term_w) term_newline();
int idx = cur_row * TERM_COLS + cur_col;
term_ch[idx] = (u8)c; term_fg[idx] = color;
cur_col++;
}
static void term_putc(char c) { term_putc_color(c, DA_TEXT_PRIMARY); }
static void term_puts_color(const char *s, u32 color) { while (*s) term_putc_color(*s++, color); }
static void term_puts(const char *s) { term_puts_color(s, DA_TEXT_PRIMARY); }
/* ---- 绘制 ---- */
static void redraw_all(void) {
da_fill_bg(&g_ac, DA_BG_PRIMARY);
da_draw_titlebar(&g_ac, "curl", (i64)g_ac.fb_w);
/* 终端字符 */
for (int r = 0; r < term_h; r++) {
for (int c = 0; c < term_w; c++) {
int idx = r * TERM_COLS + c;
if (term_ch[idx] == ' ') continue;
da_draw_char(&g_ac, term_ch[idx],
term_x + c * CHAR_STEP, term_y + r * CHAR_H,
term_fg[idx], DA_BG_PRIMARY);
}
}
/* 光标 */
if (cur_col < term_w && cur_row < term_h) {
da_fill_rect(&g_ac, term_x + cur_col * CHAR_STEP,
term_y + cur_row * CHAR_H + CHAR_H - 3, 11, 2, DA_ACCENT);
}
/* 状态栏 */
char status[64];
int p = 0;
const char *s1 = "HTTP client | Esc 退出 ";
while (*s1) status[p++] = *s1++;
u8 hh, mm; da_rtc_time(&hh, &mm);
status[p++] = '0' + hh/10; status[p++] = '0' + hh%10;
status[p++] = ':'; status[p++] = '0' + mm/10; status[p++] = '0' + mm%10;
status[p] = 0;
da_draw_statusbar(&g_ac, status, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
}
/* ---- URL 请求模拟 ---- */
static void do_http_get(const char *url) {
term_puts_color("GET ", DA_PROMPT_COLOR);
term_puts_color(url, DA_ACCENT_LIGHT);
term_putc('\n');
term_puts_color(" -> 解析主机...\n", DA_TEXT_DIM);
term_puts_color(" -> 建立 TCP 连接...\n", DA_TEXT_DIM);
/* stub: 检查 kernel_api 是否有 net 接口 */
if (!g_ac.kernel_api) {
term_puts_color(" [错误] 无内核 API\n", DA_ERROR);
return;
}
/* 尝试读取 net API — 当前为 stub */
u64 net_api = *(u64 *)((u64)g_ac.kernel_api + 0xC0);
if (!net_api) {
term_puts_color(" [错误] 需要 TCP/IP 栈支持\n", DA_ERROR);
term_puts_color(" 当前内核未实现网络协议栈。\n", DA_WARNING);
term_puts_color(" 请等待后续版本更新。\n", DA_TEXT_DIM);
return;
}
/* 如果有 net API,此处应实现真正的 HTTP GET */
term_puts_color(" [stub] 网络功能尚未实现\n", DA_WARNING);
}
/* ---- 内置命令 ---- */
static int str_eq(const char *a, const char *b) {
while (*a && *b) { if (*a != *b) return 0; a++; b++; }
return *a == *b;
}
static void cmd_help(void) {
term_puts_color("curl — 命令行 HTTP 客户端\n", DA_PROMPT_COLOR);
term_puts(" <url> 发送 HTTP GET 请求\n");
term_puts(" help 显示此帮助\n");
term_puts(" clear 清屏\n");
term_puts(" version 显示版本\n");
term_puts(" Esc 退出\n");
}
static void cmd_clear(void) { term_clear(); }
static void cmd_version(void) {
term_puts_color("Deshab curl v0.1 (stub)\n", DA_SUCCESS);
term_puts_color(" HTTP 客户端功能需要 TCP/IP 栈支持\n", DA_TEXT_DIM);
}
static void execute_command(const char *cmd) {
while (*cmd == ' ') cmd++;
if (*cmd == 0) return;
if (str_eq(cmd, "help") || str_eq(cmd, "?")) { cmd_help(); return; }
if (str_eq(cmd, "clear") || str_eq(cmd, "cls")) { cmd_clear(); return; }
if (str_eq(cmd, "version") || str_eq(cmd, "ver")) { cmd_version(); return; }
/* 非内置命令视为 URL */
if (cmd[0] == 'h' && cmd[1] == 't' && cmd[2] == 't' && cmd[3] == 'p') {
do_http_get(cmd);
} else {
term_puts_color("无效 URL: ", DA_ERROR);
term_puts_color(cmd, DA_ERROR);
term_putc('\n');
term_puts_color("请输入完整 URL (http://...)\n", DA_TEXT_DIM);
}
}
static void add_history(const char *cmd) {
if (hist_count < HIST_SIZE) {
int i;
for (i = 0; cmd[i] && i < 255; i++) history[hist_count][i] = cmd[i];
history[hist_count][i] = 0;
hist_count++;
}
hist_idx = hist_count;
}
/* ---- 输入行重绘 ---- */
static void redraw_input_line(void) {
int plen = 0;
while (PROMPT_STR[plen]) plen++;
i64 y = term_y + (i64)cur_row * CHAR_H;
da_fill_rect(&g_ac, term_x, y, (i64)term_w * CHAR_STEP, CHAR_H, DA_BG_PRIMARY);
for (int i = 0; i < plen; i++)
da_draw_char(&g_ac, PROMPT_STR[i], term_x + i * CHAR_STEP, y, DA_PROMPT_COLOR, DA_BG_PRIMARY);
for (int i = 0; i < input_len; i++)
da_draw_char(&g_ac, input_buf[i], term_x + (plen + i) * CHAR_STEP, y, DA_TEXT_PRIMARY, DA_BG_PRIMARY);
cur_col = plen + input_cursor;
da_fill_rect(&g_ac, term_x + cur_col * CHAR_STEP, y + CHAR_H - 3, 11, 2, DA_ACCENT);
}
/* ---- 主入口 ---- */
__attribute__((visibility("default")))
void dsk_entry(const da_boot_context *ctx) {
__asm__ volatile("cli");
da_slog("curl", "boot");
if (!ctx || ctx->magic != DA_BOOT_MAGIC) {
da_slog("curl", "bad context");
for(;;) __asm__("hlt");
}
da_init(&g_ac, ctx);
term_init_layout();
term_clear();
/* 启动 banner */
term_puts_color("=== Deshab curl v0.1 (stub) ===\n", DA_ACCENT);
term_puts_color("HTTP 命令行客户端\n", DA_SUCCESS);
term_puts_color("输入 URL 发送 GET 请求 | 输入 'help' 查看帮助 | Esc 退出\n", DA_TEXT_DIM);
term_puts_color("注意: 当前需要 TCP/IP 栈支持\n", DA_WARNING);
term_putc('\n');
redraw_all();
int shift = 0, e0 = 0;
for (;;) {
term_puts_color(PROMPT_STR, DA_PROMPT_COLOR);
input_len = 0; input_cursor = 0; input_buf[0] = 0;
int plen = 0; while (PROMPT_STR[plen]) plen++;
cur_col = plen;
redraw_all();
int cmd_done = 0;
while (!cmd_done) {
u8 st = inb(0x64);
if (!(st & 1)) { __asm__("pause"); continue; }
u8 data = inb(0x60);
if (st & 0x20) continue;
u8 sc = data;
if (sc == 0xE0) { e0 = 1; continue; }
if (sc == 0x2A || sc == 0x36) { shift = 1; continue; }
if (sc == 0xAA || sc == 0xB6) { shift = 0; continue; }
if (sc & 0x80) { e0 = 0; continue; }
/* Esc 退出 */
if (sc == 0x01 && !e0) {
da_slog("curl", "exit");
return;
}
if (e0) {
if (sc == 0x4B) { if (input_cursor > 0) input_cursor--; }
else if (sc == 0x4D) { if (input_cursor < input_len) input_cursor++; }
else if (sc == 0x47) { input_cursor = 0; }
else if (sc == 0x4F) { input_cursor = input_len; }
else if (sc == 0x48) {
if (hist_idx > 0) {
hist_idx--;
int i; for (i = 0; history[hist_idx][i] && i < 255; i++) input_buf[i] = history[hist_idx][i];
input_len = i; input_cursor = i; input_buf[i] = 0;
}
}
else if (sc == 0x50) {
if (hist_idx < hist_count - 1) {
hist_idx++;
int i; for (i = 0; history[hist_idx][i] && i < 255; i++) input_buf[i] = history[hist_idx][i];
input_len = i; input_cursor = i; input_buf[i] = 0;
} else if (hist_idx < hist_count) {
hist_idx = hist_count;
input_len = 0; input_cursor = 0; input_buf[0] = 0;
}
}
e0 = 0;
redraw_input_line();
continue;
}
if (sc == 0x1C) { /* Enter */
input_buf[input_len] = 0;
term_putc('\n');
if (input_len > 0) add_history(input_buf);
execute_command(input_buf);
cmd_done = 1;
continue;
}
if (sc == 0x0E) { /* Backspace */
if (input_cursor > 0) {
for (int i = input_cursor - 1; i < input_len - 1; i++)
input_buf[i] = input_buf[i+1];
input_len--; input_cursor--;
input_buf[input_len] = 0;
}
redraw_input_line();
continue;
}
char c = da_scan_to_ascii(sc, shift);
if (c && c >= 32 && c <= 126 && input_len < 255) {
for (int i = input_len; i > input_cursor; i--)
input_buf[i] = input_buf[i-1];
input_buf[input_cursor++] = c;
input_len++;
redraw_input_line();
}
}
redraw_all();
}
}
+374
View File
@@ -0,0 +1,374 @@
/* desktop_app.h — Deshab 独立工具应用公共头文件
*
* 独立工具应用运行在 DSK 加载的 .elf 中,全屏独占 framebuffer。
* 每个 .elf 实现 dsk_entry(dsk_boot_context *ctx) 入口点。
* 通过 kernel_api 访问块设备、网络等内核服务。
* PS/2 键盘/鼠标通过端口 I/O 直接轮询。
*/
#ifndef DESHAB_DESKTOP_APP_H
#define DESHAB_DESKTOP_APP_H
/* 基础类型 */
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef long long i64;
/* 端口 I/O */
static inline void outb(u16 port, u8 val) { __asm__ volatile("outb %0,%1"::"a"(val),"Nd"(port)); }
static inline u8 inb(u16 port) { u8 v; __asm__ volatile("inb %1,%0":"=a"(v):"Nd"(port)); return v; }
/* COM1 串口日志 */
#define DA_COM1 0x3F8
static inline void da_sputc(char c) { for(unsigned i=0;i<100000;i++){if(inb(DA_COM1+5)&0x20)break;} outb(DA_COM1,(u8)c); }
static inline void da_swrite(const char *s) { while(*s){if(*s=='\n')da_sputc('\r');da_sputc(*s++);} }
static inline void da_slog(const char *tag, const char *msg) { da_swrite("["); da_swrite(tag); da_swrite("] "); da_swrite(msg); da_swrite("\n"); }
/* dsk_boot_context 定义(与 dsk.h 一致,自包含避免依赖) */
#define DA_BOOT_MAGIC 0x44534B31424F4F54ULL
typedef struct {
u64 magic; u32 abi_version; u32 size; u64 flags;
u64 hhdm_offset; u64 rsdp_address;
u64 framebuffer_address; u64 framebuffer_width; u64 framebuffer_height;
u64 framebuffer_pitch; u32 framebuffer_bpp; u32 reserved_fb;
u64 boot_modules_response;
u64 dkm_kernel_api; u64 dkm_driver_table; u64 dkm_driver_count;
u64 utsm_state; u64 drr_state;
u64 memory_map; u64 memory_map_count; u64 memory_map_entry_size;
u64 kernel_stack_top; u64 reserved[8];
} da_boot_context;
/* block_read 函数类型 */
typedef int (*da_block_read_fn)(void *ctx, u64 lba, u32 count, void *buffer);
/* 应用上下文 — 从 dsk_boot_context 提取的常用数据 */
typedef struct {
u32 *fb; /* framebuffer 基址 */
u64 fb_w; /* 宽度 */
u64 fb_h; /* 高度 */
u64 fb_pitch; /* 行宽(字节) */
da_block_read_fn block_read;
const void *kernel_api;
} da_app_context;
/* 从 dsk_boot_context 初始化 da_app_context */
static inline void da_init(da_app_context *ac, const da_boot_context *ctx) {
ac->fb = (u32 *)ctx->framebuffer_address;
ac->fb_w = ctx->framebuffer_width;
ac->fb_h = ctx->framebuffer_height;
ac->fb_pitch = ctx->framebuffer_pitch;
ac->kernel_api = (const void *)ctx->dkm_kernel_api;
/* block_read 从 kernel_api + 0xA8 获取 */
u64 api = ctx->dkm_kernel_api;
ac->block_read = (da_block_read_fn)*(u64 *)(api + 0xA8);
if (ac->block_read)
ac->block_read = (da_block_read_fn)*(u64 *)((u64)ac->block_read + 16);
}
/* ========== 帧缓冲绘制 ========== */
static inline void da_pixel(da_app_context *ac, i64 x, i64 y, u32 color) {
if (x < 0 || (u64)x >= ac->fb_w || y < 0 || (u64)y >= ac->fb_h) return;
u32 *line = (u32 *)((u8 *)ac->fb + (u64)y * ac->fb_pitch);
line[x] = color;
}
static inline u32 da_pixel_read(da_app_context *ac, i64 x, i64 y) {
if (x < 0 || (u64)x >= ac->fb_w || y < 0 || (u64)y >= ac->fb_h) return 0;
u32 *line = (u32 *)((u8 *)ac->fb + (u64)y * ac->fb_pitch);
return line[x];
}
static inline u32 da_blend(u32 bg, u32 fg, u32 alpha) {
u32 na = 256 - alpha;
u32 r = ((bg & 0xFF) * na + (fg & 0xFF) * alpha) >> 8;
u32 g = (((bg >> 8) & 0xFF) * na + ((fg >> 8) & 0xFF) * alpha) >> 8;
u32 b = (((bg >> 16) & 0xFF) * na + ((fg >> 16) & 0xFF) * alpha) >> 8;
return 0xFF000000u | (b << 16) | (g << 8) | r;
}
static inline void da_fill_rect(da_app_context *ac, i64 x, i64 y, i64 w, i64 h, u32 color) {
for (i64 r = 0; r < h; r++) {
i64 yy = y + r;
if (yy < 0 || (u64)yy >= ac->fb_h) continue;
u32 *line = (u32 *)((u8 *)ac->fb + (u64)yy * ac->fb_pitch);
for (i64 c = 0; c < w; c++) {
i64 xx = x + c;
if (xx < 0 || (u64)xx >= ac->fb_w) continue;
line[xx] = color;
}
}
}
static inline void da_fill_bg(da_app_context *ac, u32 color) {
da_fill_rect(ac, 0, 0, (i64)ac->fb_w, (i64)ac->fb_h, color);
}
/* 圆角矩形 */
static inline void da_fill_rounded_rect(da_app_context *ac, i64 x, i64 y, i64 w, i64 h, u32 color, i64 radius) {
i64 r = radius;
if (r > w/2) r = w/2;
if (r > h/2) r = h/2;
da_fill_rect(ac, x+r, y, w-2*r, h, color);
da_fill_rect(ac, x, y+r, r, h-2*r, color);
da_fill_rect(ac, x+w-r, y+r, r, h-2*r, color);
for (i64 dy = 0; dy < r; dy++)
for (i64 dx = 0; dx < r; dx++)
if (dx*dx+dy*dy <= r*r) {
da_pixel(ac, x+r-1-dx, y+r-1-dy, color);
da_pixel(ac, x+w-r+dx, y+r-1-dy, color);
da_pixel(ac, x+r-1-dx, y+h-r+dy, color);
da_pixel(ac, x+w-r+dx, y+h-r+dy, color);
}
}
static inline void da_rect_outline(da_app_context *ac, i64 x, i64 y, i64 w, i64 h, u32 color, i64 radius) {
i64 r = radius;
if (r > w/2) r = w/2;
if (r > h/2) r = h/2;
for (i64 c = r; c < w-r; c++) { da_pixel(ac, x+c, y, color); da_pixel(ac, x+c, y+h-1, color); }
for (i64 rr = r; rr < h-r; rr++) { da_pixel(ac, x, y+rr, color); da_pixel(ac, x+w-1, y+rr, color); }
for (i64 dy = 0; dy <= r; dy++)
for (i64 dx = 0; dx <= r; dx++) {
i64 d2 = dx*dx+dy*dy;
if (d2 <= r*r && d2 > (r-1)*(r-1)) {
da_pixel(ac, x+r-dx, y+r-dy, color);
da_pixel(ac, x+w-1-r+dx, y+r-dy, color);
da_pixel(ac, x+r-dx, y+h-1-r+dy, color);
da_pixel(ac, x+w-1-r+dx, y+h-1-r+dy, color);
}
}
}
/* ========== ASCII 字体渲染 ==========
*
* 使用方法:先 #include ascii_bitmaps.c(定义 static g_ascii/g_ascii_w/g_ascii_h),
* 再 #include desktop_app.h。da_draw_char/da_draw_string 直接引用 g_ascii。
* 与 deshab_ui.h 使用相同的包含顺序约定。
*/
static inline void da_draw_char(da_app_context *ac, char ch, i64 x, i64 y, u32 fg, u32 bg) {
u32 idx = (u32)(ch - ' ');
if (idx > 94) idx = 0;
const u8 *glyph = g_ascii[idx];
i64 gw = g_ascii_w, gh = g_ascii_h;
for (i64 r = 0; r < gh; r++) {
i64 yy = y + r;
if (yy < 0 || (u64)yy >= ac->fb_h) continue;
u32 *line = (u32 *)((u8 *)ac->fb + (u64)yy * ac->fb_pitch);
for (i64 c = 0; c < gw; c++) {
u8 a = glyph[r * gw + c];
if (a == 0) continue;
i64 xx = x + c;
if (xx < 0 || (u64)xx >= ac->fb_w) continue;
line[xx] = (a == 255) ? fg : da_blend(bg, fg, a);
}
}
}
static inline void da_draw_string(da_app_context *ac, const char *s, i64 x, i64 y, u32 fg, u32 bg, i64 step) {
i64 cx = x;
while (*s) { da_draw_char(ac, *s, cx, y, fg, bg); cx += step; s++; }
}
/* ========== 鼠标光标 ========== */
#define DA_CURSOR_SIZE 24
static const u8 da_cursor_shape[24][24] = {
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
};
typedef struct {
u32 save[DA_CURSOR_SIZE * DA_CURSOR_SIZE];
int saved;
int old_x, old_y;
int mx, my;
int btn;
} da_cursor;
static inline void da_cursor_init(da_cursor *c, i64 fb_w, i64 fb_h) {
for (int i = 0; i < DA_CURSOR_SIZE*DA_CURSOR_SIZE; i++) c->save[i] = 0;
c->saved = 0; c->old_x = -1; c->old_y = -1;
c->mx = (int)(fb_w / 2); c->my = (int)(fb_h / 2); c->btn = 0;
}
static inline void da_cursor_save(da_app_context *ac, da_cursor *c) {
for (int r = 0; r < DA_CURSOR_SIZE; r++)
for (int col = 0; col < DA_CURSOR_SIZE; col++) {
int x = c->mx + col, y = c->my + r;
c->save[r * DA_CURSOR_SIZE + col] = da_pixel_read(ac, x, y);
}
c->saved = 1; c->old_x = c->mx; c->old_y = c->my;
}
static inline void da_cursor_restore(da_app_context *ac, da_cursor *c) {
if (!c->saved) return;
for (int r = 0; r < DA_CURSOR_SIZE; r++)
for (int col = 0; col < DA_CURSOR_SIZE; col++) {
int x = c->old_x + col, y = c->old_y + r;
da_pixel(ac, x, y, c->save[r * DA_CURSOR_SIZE + col]);
}
c->saved = 0;
}
static inline void da_cursor_draw(da_app_context *ac, da_cursor *c, u32 color) {
for (int r = 0; r < DA_CURSOR_SIZE; r++)
for (int col = 0; col < DA_CURSOR_SIZE; col++)
if (da_cursor_shape[r][col])
da_pixel(ac, c->mx + col, c->my + r, color);
}
/* ========== PS/2 鼠标 ========== */
typedef struct {
u8 buf[3];
int idx;
int has_pkt;
} da_mouse;
static inline void da_mouse_init(void) {
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
outb(0x64, 0xA8);
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
outb(0x64, 0x20);
for (int t = 0; t < 100000; t++) { if (inb(0x64) & 1) break; }
u8 cfg = inb(0x60);
cfg |= 0x02; cfg &= ~0x20;
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
outb(0x64, 0x60);
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
outb(0x60, cfg);
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
outb(0x64, 0xD4);
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
outb(0x60, 0xFF);
for (int t = 0; t < 100000; t++) { if (inb(0x64) & 1) break; }
inb(0x60);
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
outb(0x64, 0xD4);
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
outb(0x60, 0xF4);
for (int t = 0; t < 100000; t++) { if (inb(0x64) & 1) break; }
inb(0x60);
}
static inline int da_mouse_poll(da_mouse *m, da_cursor *c, i64 fb_w, i64 fb_h) {
u8 st = inb(0x64);
if (!(st & 1)) return 0;
u8 data = inb(0x60);
if (!(st & 0x20)) return 0;
m->buf[m->idx++] = data;
if (m->idx < 3) return 0;
m->idx = 0;
if (!(m->buf[0] & 0x08)) return 0;
int dx = (int)(signed char)m->buf[1];
int dy = (int)(signed char)m->buf[2];
if (m->buf[0] & 0x10) dx = -dx;
if (!(m->buf[0] & 0x20)) dy = -dy; else dy = -dy;
dy = -dy;
c->mx += dx; c->my += dy;
if (c->mx < 0) c->mx = 0;
if (c->my < 0) c->my = 0;
if (c->mx >= (int)fb_w - DA_CURSOR_SIZE) c->mx = (int)fb_w - DA_CURSOR_SIZE;
if (c->my >= (int)fb_h - DA_CURSOR_SIZE) c->my = (int)fb_h - DA_CURSOR_SIZE;
c->btn = (m->buf[0] & 0x01) ? 1 : 0;
m->has_pkt = 1;
return 1;
}
/* ========== 键盘 ========== */
static inline char da_scan_to_ascii(u8 sc, int shift) {
static const char normal[58] = {
0, 27, '1','2','3','4','5','6','7','8','9','0','-','=', 8, '\t',
'q','w','e','r','t','y','u','i','o','p','[',']','\n',0,
'a','s','d','f','g','h','j','k','l',';','\'', '`',0,'\\',
'z','x','c','v','b','n','m',',','.','/',0,'*',0,' '
};
static const char shifted[58] = {
0, 27, '!','@','#','$','%','^','&','*','(',')','_','+', 8, '\t',
'Q','W','E','R','T','Y','U','I','O','P','{','}','\n',0,
'A','S','D','F','G','H','J','K','L',':','"','~',0,'|',
'Z','X','C','V','B','N','M','<','>','?',0,'*',0,' '
};
if (sc >= 58) return 0;
return shift ? shifted[sc] : normal[sc];
}
/* ========== 窗口概念 ========== */
/* 独立工具全屏运行时的窗口装饰:标题栏 + 关闭按钮 */
#define DA_TITLEBAR_H 28
#define DA_BORDER_W 2
#define DA_STATUSBAR_H 20
/* 色板引用(与 deshab_ui.h 一致) */
#define DA_BG_PRIMARY 0xFF0A1428u /* DP_ABYSS_900 */
#define DA_BG_SECONDARY 0xFF0F1E38u /* DP_ABYSS_800 */
#define DA_BG_TERTIARY 0xFF162848u /* DP_ABYSS_700 */
#define DA_TEXT_PRIMARY 0xFFE8E8F0u /* DP_NEUTRAL_700 */
#define DA_TEXT_DIM 0xFF6A6A88u /* DP_NEUTRAL_300 */
#define DA_ACCENT 0xFF00A8CCu /* DP_SEAL_500 */
#define DA_ACCENT_LIGHT 0xFF44CCF0u /* DP_SEAL_300 */
#define DA_BORDER 0xFF284888u /* DP_ABYSS_500 */
#define DA_BORDER_FOCUS 0xFF44CCF0u /* DP_SEAL_300 */
#define DA_CURSOR_COLOR 0xFF00A8CCu /* DP_SEAL_500 */
#define DA_PROMPT_COLOR 0xFF44CCF0u /* DP_SEAL_300 */
#define DA_SUCCESS 0xFF40C880u /* DP_SUCCESS */
#define DA_ERROR 0xFFFF4466u /* DP_ERROR */
#define DA_WARNING 0xFFF0A030u /* DP_WARNING */
static inline void da_draw_titlebar(da_app_context *ac, const char *title, i64 w) {
da_fill_rect(ac, 0, 0, w, DA_TITLEBAR_H, DA_BG_SECONDARY);
da_draw_string(ac, title, 8, (DA_TITLEBAR_H - 18) / 2, DA_TEXT_PRIMARY, DA_BG_SECONDARY, 12);
/* 关闭按钮 */
da_fill_rounded_rect(ac, w - 28, 4, 24, 20, DA_ERROR, 4);
da_draw_string(ac, "X", w - 20, 5, 0xFF0A1428u, DA_ERROR, 12);
/* 底部分隔线 */
da_fill_rect(ac, 0, DA_TITLEBAR_H, w, 1, DA_ACCENT);
}
static inline void da_draw_statusbar(da_app_context *ac, const char *text, i64 w, i64 h) {
i64 y = h - DA_STATUSBAR_H;
da_fill_rect(ac, 0, y, w, DA_STATUSBAR_H, DA_BG_SECONDARY);
da_draw_string(ac, text, 4, y + 1, DA_TEXT_DIM, DA_BG_SECONDARY, 12);
/* 顶部分隔线 */
da_fill_rect(ac, 0, y, w, 1, DA_BORDER);
}
/* ========== RTC 时钟 ========== */
static inline void da_rtc_time(u8 *h, u8 *m) {
outb(0x70, 4); *h = inb(0x71);
outb(0x70, 2); *m = inb(0x71);
*h = (u8)((*h >> 4) * 10 + (*h & 0xF));
*m = (u8)((*m >> 4) * 10 + (*m & 0xF));
}
/* ========== memset/memcpy ========== */
static inline void *da_memset(void *d, int c, u64 n) { u8 *p=(u8*)d; while(n--)*p++=(u8)c; return d; }
static inline void *da_memcpy(void *d, const void *s, u64 n) { u8 *dd=(u8*)d; const u8 *ss=(const u8*)s; while(n--)*dd++=*ss++; return d; }
#endif /* DESHAB_DESKTOP_APP_H */
+25
View File
@@ -0,0 +1,25 @@
CC := clang
LD := ld.lld
ROOT := ../../..
OUT := $(ROOT)/SYSTEM/system/deshab64/tools/editor.elf
CFLAGS := -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fpie -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -I.. -I../../UTSM/include -O2
LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld
C_SOURCES := main.c
OBJECTS := $(C_SOURCES:.c=.o)
.PHONY: all clean
all: $(OUT)
$(OUT): $(OBJECTS) linker.ld
@if not exist "$(ROOT)\SYSTEM\system\deshab64\tools" mkdir "$(ROOT)\SYSTEM\system\deshab64\tools"
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }"
+16
View File
@@ -0,0 +1,16 @@
ENTRY(dsk_entry)
PHDRS
{
text PT_LOAD FLAGS(7);
rodata PT_LOAD FLAGS(4);
data PT_LOAD FLAGS(7);
}
SECTIONS
{
. = 0x1000;
.text : ALIGN(4K) { *(.text*) } :text
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
.data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data
}
+238
View File
@@ -0,0 +1,238 @@
/* Deshab Editor — 全屏文本编辑器
* PS/2 键盘输入,行号 + 光标 + 垂直滚动。
* Ctrl+O 打开文件,Ctrl+S 保存(暂不支持),Esc 退出。
*/
#include "../../firstInit/ascii_bitmaps.c"
#include "../desktop_app.h"
#define CHAR_STEP 12
#define CHAR_H 18
#define EDITOR_BUF_SIZE 65536
static da_app_context g_ac;
static char text_buf[EDITOR_BUF_SIZE];
static int text_len = 0;
static int cursor_pos = 0;
static int scroll_y = 0;
static int cursor_line = 0;
static int cursor_col = 0;
static int modified = 0;
static char filename[64] = "untitled";
static int ctrl_down = 0;
static int line_start(int line) {
int l = 0;
for (int i = 0; i < text_len; i++) {
if (l == line) return i;
if (text_buf[i] == '\n') l++;
}
return text_len;
}
static void recalc_cursor(void) {
cursor_line = 0; cursor_col = 0;
for (int i = 0; i < cursor_pos && i < text_len; i++) {
if (text_buf[i] == '\n') { cursor_line++; cursor_col = 0; }
else cursor_col++;
}
}
static void init_default_text(void) {
const char *sample = "# Deshab Editor\n#\n\nStart typing here...\n\nCtrl+O: Open Ctrl+S: Save Esc: Exit\n";
while (*sample && text_len < EDITOR_BUF_SIZE - 1)
text_buf[text_len++] = *sample++;
cursor_pos = text_len;
recalc_cursor();
}
static void redraw_all(void) {
da_fill_bg(&g_ac, DA_BG_SECONDARY);
char title[80];
int p = 0;
const char *t1 = "Editor - ";
while (*t1) title[p++] = *t1++;
for (int i = 0; filename[i] && p < 70; i++) title[p++] = filename[i];
if (modified) { title[p++] = ' '; title[p++] = '*'; }
title[p] = 0;
da_draw_titlebar(&g_ac, title, (i64)g_ac.fb_w);
int margin_x = 8;
int top_y = DA_TITLEBAR_H + 4;
int line_num_w = 5 * CHAR_STEP + 8;
int text_x = margin_x + line_num_w;
int visible_rows = ((int)g_ac.fb_h - top_y - DA_STATUSBAR_H - 8) / CHAR_H;
int visible_cols = ((int)g_ac.fb_w - text_x - 8) / CHAR_STEP;
/* 自动滚动 */
if (cursor_line < scroll_y) scroll_y = cursor_line;
if (cursor_line >= scroll_y + visible_rows) scroll_y = cursor_line - visible_rows + 1;
/* 渲染可见行 */
int line = 0, pos = 0;
for (; pos <= text_len && line < scroll_y + visible_rows; ) {
if (line >= scroll_y) {
int screen_y = top_y + (line - scroll_y) * CHAR_H;
/* 行号 */
char ln[8];
ln[0] = '0' + ((line+1)/100)%10;
ln[1] = '0' + ((line+1)/10)%10;
ln[2] = '0' + (line+1)%10;
ln[3] = 0;
da_draw_string(&g_ac, ln, margin_x, screen_y, DA_TEXT_DIM, DA_BG_SECONDARY, CHAR_STEP);
/* 分隔线 */
da_fill_rect(&g_ac, margin_x + line_num_w - 2, screen_y, 1, CHAR_H, DA_BORDER);
/* 文本 */
int col = 0;
while (pos < text_len && text_buf[pos] != '\n' && col < visible_cols) {
da_draw_char(&g_ac, text_buf[pos],
text_x + col * CHAR_STEP, screen_y,
DA_TEXT_PRIMARY, DA_BG_SECONDARY);
col++; pos++;
}
} else {
while (pos < text_len && text_buf[pos] != '\n') pos++;
}
if (pos < text_len && text_buf[pos] == '\n') pos++;
line++;
if (pos >= text_len) {
/* 空行也画光标 */
if (line > cursor_line) break;
}
}
/* 光标 */
if (cursor_line >= scroll_y && cursor_line < scroll_y + visible_rows) {
int cy = top_y + (cursor_line - scroll_y) * CHAR_H;
int cx = text_x + cursor_col * CHAR_STEP;
da_fill_rect(&g_ac, cx, cy, 2, CHAR_H, DA_ACCENT_LIGHT);
}
/* 状态栏 */
char status[64]; p = 0;
const char *s1 = "L:"; while (*s1) status[p++] = *s1++;
status[p++] = '0' + (cursor_line+1)/10%10; status[p++] = '0' + (cursor_line+1)%10;
const char *s2 = " C:"; while (*s2) status[p++] = *s2++;
status[p++] = '0' + (cursor_col+1)/10%10; status[p++] = '0' + (cursor_col+1)%10;
if (modified) { const char *m = " *"; while (*m) status[p++] = *m++; }
status[p] = 0;
da_draw_statusbar(&g_ac, status, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
}
__attribute__((visibility("default")))
void dsk_entry(const da_boot_context *ctx) {
__asm__ volatile("cli");
da_slog("editor", "boot");
if (!ctx || ctx->magic != DA_BOOT_MAGIC) {
da_slog("editor", "bad context");
for(;;) __asm__("hlt");
}
da_init(&g_ac, ctx);
init_default_text();
redraw_all();
int shift = 0, e0 = 0;
for (;;) {
u8 st = inb(0x64);
if (!(st & 1)) { __asm__("pause"); continue; }
u8 data = inb(0x60);
if (st & 0x20) continue;
u8 sc = data;
if (sc == 0xE0) { e0 = 1; continue; }
if (sc == 0x2A || sc == 0x36) { shift = 1; continue; }
if (sc == 0xAA || sc == 0xB6) { shift = 0; continue; }
if (sc == 0x1D) { ctrl_down = 1; continue; }
if (sc == 0x9D) { ctrl_down = 0; continue; }
if (sc & 0x80) { e0 = 0; continue; }
/* Esc 退出 */
if (sc == 0x01 && !e0) { da_slog("editor", "exit"); return; }
if (e0) {
if (sc == 0x48) { /* Up */
if (cursor_line > 0) {
int prev = line_start(cursor_line - 1);
cursor_pos = prev;
for (int i = 0; i < cursor_col && cursor_pos < text_len && text_buf[cursor_pos] != '\n'; i++)
cursor_pos++;
}
} else if (sc == 0x50) { /* Down */
int next = line_start(cursor_line + 1);
cursor_pos = next;
for (int i = 0; i < cursor_col && cursor_pos < text_len && text_buf[cursor_pos] != '\n'; i++)
cursor_pos++;
} else if (sc == 0x4B) { /* Left */
if (cursor_pos > 0) cursor_pos--;
} else if (sc == 0x4D) { /* Right */
if (cursor_pos < text_len) cursor_pos++;
} else if (sc == 0x47) { /* Home */
cursor_pos = line_start(cursor_line);
} else if (sc == 0x4F) { /* End */
cursor_pos = line_start(cursor_line);
while (cursor_pos < text_len && text_buf[cursor_pos] != '\n') cursor_pos++;
} else if (sc == 0x49) { /* PageUp */
for (int i = 0; i < 10 && cursor_line > 0; i++) {
cursor_pos = line_start(cursor_line - 1);
recalc_cursor();
}
} else if (sc == 0x51) { /* PageDown */
for (int i = 0; i < 10; i++) {
int next = line_start(cursor_line + 1);
if (next >= text_len) break;
cursor_pos = next;
recalc_cursor();
}
}
e0 = 0;
recalc_cursor();
redraw_all();
continue;
}
if (sc == 0x1C) { /* Enter */
if (text_len < EDITOR_BUF_SIZE - 1) {
for (int i = text_len; i > cursor_pos; i--) text_buf[i] = text_buf[i-1];
text_buf[cursor_pos] = '\n';
text_len++; cursor_pos++;
modified = 1;
}
} else if (sc == 0x0E) { /* Backspace */
if (cursor_pos > 0) {
for (int i = cursor_pos - 1; i < text_len - 1; i++) text_buf[i] = text_buf[i+1];
text_len--; cursor_pos--;
modified = 1;
}
} else if (sc == 0x53 && e0) { /* Delete */
if (cursor_pos < text_len) {
for (int i = cursor_pos; i < text_len - 1; i++) text_buf[i] = text_buf[i+1];
text_len--;
modified = 1;
}
} else {
/* Ctrl+O: 打开文件(提示暂不支持) */
if (ctrl_down && (sc == 0x18)) {
/* Ctrl+O - stub */
da_slog("editor", "Ctrl+O - open not yet");
continue;
}
/* Ctrl+S: 保存(提示暂不支持) */
if (ctrl_down && (sc == 0x1F)) {
da_slog("editor", "Ctrl+S - save not yet");
modified = 0;
continue;
}
char c = da_scan_to_ascii(sc, shift);
if (c && c >= 32 && c <= 126 && text_len < EDITOR_BUF_SIZE - 1) {
for (int i = text_len; i > cursor_pos; i--) text_buf[i] = text_buf[i-1];
text_buf[cursor_pos] = c;
text_len++; cursor_pos++;
modified = 1;
}
}
recalc_cursor();
redraw_all();
}
}
+25
View File
@@ -0,0 +1,25 @@
CC := clang
LD := ld.lld
ROOT := ../../..
OUT := $(ROOT)/SYSTEM/system/deshab64/tools/fileman.elf
CFLAGS := -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fpie -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -I.. -I../../UTSM/include -O2
LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld
C_SOURCES := main.c
OBJECTS := $(C_SOURCES:.c=.o)
.PHONY: all clean
all: $(OUT)
$(OUT): $(OBJECTS) linker.ld
@if not exist "$(ROOT)\SYSTEM\system\deshab64\tools" mkdir "$(ROOT)\SYSTEM\system\deshab64\tools"
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }"
+16
View File
@@ -0,0 +1,16 @@
ENTRY(dsk_entry)
PHDRS
{
text PT_LOAD FLAGS(7);
rodata PT_LOAD FLAGS(4);
data PT_LOAD FLAGS(7);
}
SECTIONS
{
. = 0x1000;
.text : ALIGN(4K) { *(.text*) } :text
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
.data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data
}
+230
View File
@@ -0,0 +1,230 @@
/* Deshab File Manager — 全屏文件管理器
* 文件列表视图,鼠标点击选择,键盘导航。
* FAT32 根目录遍历,Esc 退出。
*/
#include "../../firstInit/ascii_bitmaps.c"
#include "../desktop_app.h"
#define CHAR_STEP 12
#define CHAR_H 18
#define MAX_FILES 256
static da_app_context g_ac;
static da_cursor g_cursor;
static da_mouse g_mouse;
typedef struct {
char name[13]; /* 8.3 */
int is_dir;
u32 size;
} file_entry;
static file_entry files[MAX_FILES];
static int file_count = 0;
static int selected = 0;
static int scroll_off = 0;
static void load_root_dir(void) {
file_count = 0;
if (!g_ac.block_read) return;
u8 bpb[512];
if (g_ac.block_read(0, 0, 1, bpb) != 0) return;
if (bpb[510] != 0x55 || bpb[511] != 0xAA) return;
u32 root_cluster = *(u32 *)(bpb + 44);
u8 sectors_per_cluster = bpb[13];
u32 fat_start = *(u16 *)(bpb + 14);
u32 data_start = fat_start + (*(u16 *)(bpb + 22)) * 2;
u8 dir_buf[8192];
u32 lba = data_start + (root_cluster - 2) * sectors_per_cluster;
if (g_ac.block_read(0, lba, sectors_per_cluster, dir_buf) != 0) return;
for (int i = 0; i < (int)(sectors_per_cluster * 512) && file_count < MAX_FILES; i += 32) {
u8 first = dir_buf[i];
if (first == 0x00) break;
if (first == 0xE5) continue;
if (dir_buf[i+11] & 0x08) continue;
if (dir_buf[i+11] & 0x10) continue; /* 暂跳过目录 */
file_entry *f = &files[file_count];
f->is_dir = (dir_buf[i+11] & 0x10) ? 1 : 0;
f->size = *(u32 *)(dir_buf + i + 28);
int p = 0;
for (int j = 0; j < 8 && dir_buf[i+j] != ' '; j++)
f->name[p++] = (char)dir_buf[i+j];
if (dir_buf[i+8] != ' ') {
f->name[p++] = '.';
for (int j = 8; j < 11 && dir_buf[i+j] != ' '; j++)
f->name[p++] = (char)dir_buf[i+j];
}
f->name[p] = 0;
file_count++;
}
}
static void redraw_all(void) {
da_fill_bg(&g_ac, DA_BG_PRIMARY);
da_draw_titlebar(&g_ac, "Files", (i64)g_ac.fb_w);
int margin = 12;
int top_y = DA_TITLEBAR_H + 4;
int row_h = CHAR_H + 6;
int visible_rows = ((int)g_ac.fb_h - top_y - DA_STATUSBAR_H - 8) / row_h;
int col_name_x = margin + 32;
int col_size_x = (int)g_ac.fb_w - 120;
int col_type_x = (int)g_ac.fb_w - 60;
/* 列标题 */
da_draw_string(&g_ac, "Name", col_name_x, top_y, DA_TEXT_DIM, DA_BG_PRIMARY, CHAR_STEP);
da_draw_string(&g_ac, "Size", col_size_x, top_y, DA_TEXT_DIM, DA_BG_PRIMARY, CHAR_STEP);
da_draw_string(&g_ac, "Type", col_type_x, top_y, DA_TEXT_DIM, DA_BG_PRIMARY, CHAR_STEP);
da_fill_rect(&g_ac, margin, top_y + CHAR_H, (i64)g_ac.fb_w - margin * 2, 1, DA_BORDER);
/* 文件列表 */
if (scroll_off < 0) scroll_off = 0;
if (scroll_off > file_count - visible_rows && file_count > visible_rows) scroll_off = file_count - visible_rows;
if (scroll_off < 0) scroll_off = 0;
for (int i = 0; i < visible_rows; i++) {
int idx = scroll_off + i;
if (idx >= file_count) break;
int y = top_y + CHAR_H + 6 + i * row_h;
/* 选中高亮 */
if (idx == selected) {
da_fill_rect(&g_ac, margin, y - 2, (i64)g_ac.fb_w - margin * 2, row_h, DA_BG_TERTIARY);
da_rect_outline(&g_ac, margin, y - 2, (i64)g_ac.fb_w - margin * 2, row_h, DA_ACCENT, 4);
}
/* 图标 */
u32 icon_color = files[idx].is_dir ? DA_WARNING : DA_ACCENT;
da_fill_rounded_rect(&g_ac, margin + 4, y, 24, 16, icon_color, 3);
/* 文件名 */
u32 name_color = (idx == selected) ? DA_ACCENT_LIGHT : DA_TEXT_PRIMARY;
da_draw_string(&g_ac, files[idx].name, col_name_x, y, name_color,
(idx == selected) ? DA_BG_TERTIARY : DA_BG_PRIMARY, CHAR_STEP);
/* 大小 */
if (!files[idx].is_dir) {
char sz[16]; int p = 0;
u32 s = files[idx].size;
if (s == 0) { sz[p++] = '0'; }
else { char tmp[12]; int tl = 0; while(s>0){tmp[tl++]='0'+(s%10);s/=10;} for(int j=tl-1;j>=0;j--)sz[p++]=tmp[j]; }
sz[p++] = ' '; sz[p++] = 'B'; sz[p] = 0;
da_draw_string(&g_ac, sz, col_size_x, y, DA_TEXT_DIM,
(idx == selected) ? DA_BG_TERTIARY : DA_BG_PRIMARY, CHAR_STEP);
} else {
da_draw_string(&g_ac, "--", col_size_x, y, DA_TEXT_DIM,
(idx == selected) ? DA_BG_TERTIARY : DA_BG_PRIMARY, CHAR_STEP);
}
/* 类型 */
const char *type = files[idx].is_dir ? "DIR" : "FILE";
da_draw_string(&g_ac, type, col_type_x, y, DA_TEXT_DIM,
(idx == selected) ? DA_BG_TERTIARY : DA_BG_PRIMARY, CHAR_STEP);
}
if (file_count == 0) {
da_draw_string(&g_ac, "No files found (empty or no block device)",
margin + 20, top_y + 60, DA_TEXT_DIM, DA_BG_PRIMARY, CHAR_STEP);
}
/* 状态栏 */
char status[64]; int p = 0;
const char *p1 = "/ "; while (*p1) status[p++] = *p1++;
status[p++] = '0' + file_count / 10; status[p++] = '0' + file_count % 10;
const char *p2 = " items"; while (*p2) status[p++] = *p2++;
status[p] = 0;
da_draw_statusbar(&g_ac, status, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
/* 鼠标光标 */
da_cursor_save(&g_ac, &g_cursor);
da_cursor_draw(&g_ac, &g_cursor, DA_CURSOR_COLOR);
}
__attribute__((visibility("default")))
void dsk_entry(const da_boot_context *ctx) {
__asm__ volatile("cli");
da_slog("fileman", "boot");
if (!ctx || ctx->magic != DA_BOOT_MAGIC) {
da_slog("fileman", "bad context");
for(;;) __asm__("hlt");
}
da_init(&g_ac, ctx);
da_cursor_init(&g_cursor, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
da_mouse_init();
load_root_dir();
redraw_all();
int e0 = 0, shift = 0; (void)shift;
for (;;) {
int need_redraw = 0;
/* 鼠标轮询 */
if (da_mouse_poll(&g_mouse, &g_cursor, (i64)g_ac.fb_w, (i64)g_ac.fb_h)) {
need_redraw = 1;
if (g_cursor.btn) {
/* 点击选择文件 */
int margin = 12;
int top_y = DA_TITLEBAR_H + 4;
int row_h = CHAR_H + 6;
int click_y = g_cursor.my - top_y - CHAR_H - 6;
if (click_y >= 0) {
int idx = click_y / row_h + scroll_off;
if (idx >= 0 && idx < file_count) {
selected = idx;
need_redraw = 1;
}
}
}
}
/* 键盘轮询 */
u8 st = inb(0x64);
if ((st & 1) && !(st & 0x20)) {
u8 data = inb(0x60);
u8 sc = data;
if (sc == 0xE0) { e0 = 1; goto skip; }
if (sc == 0x2A || sc == 0x36) { shift = 1; goto skip; }
if (sc == 0xAA || sc == 0xB6) { shift = 0; goto skip; }
if (sc & 0x80) { e0 = 0; goto skip; }
if (sc == 0x01) { da_slog("fileman", "exit"); return; }
if (e0) {
if (sc == 0x48) { /* Up */
if (selected > 0) selected--;
need_redraw = 1;
} else if (sc == 0x50) { /* Down */
if (selected < file_count - 1) selected++;
need_redraw = 1;
}
e0 = 0;
goto skip;
}
if (sc == 0x1C) { /* Enter - 打开 */
if (selected >= 0 && selected < file_count) {
da_slog("fileman", "open file (stub)");
}
need_redraw = 1;
} else if (sc == 0x0E) { /* Backspace - 返回上级 */
da_slog("fileman", "back (root only)");
need_redraw = 1;
}
}
skip:
if (need_redraw) {
da_cursor_restore(&g_ac, &g_cursor);
redraw_all();
}
__asm__("pause");
}
}
+25
View File
@@ -0,0 +1,25 @@
CC := clang
LD := ld.lld
ROOT := ../../..
OUT := $(ROOT)/SYSTEM/system/deshab64/tools/ping.elf
CFLAGS := -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fpie -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -I.. -I../../UTSM/include -O2
LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld
C_SOURCES := main.c
OBJECTS := $(C_SOURCES:.c=.o)
.PHONY: all clean
all: $(OUT)
$(OUT): $(OBJECTS) linker.ld
@if not exist "$(ROOT)\SYSTEM\system\deshab64\tools" mkdir "$(ROOT)\SYSTEM\system\deshab64\tools"
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }"
+16
View File
@@ -0,0 +1,16 @@
ENTRY(dsk_entry)
PHDRS
{
text PT_LOAD FLAGS(7);
rodata PT_LOAD FLAGS(4);
data PT_LOAD FLAGS(7);
}
SECTIONS
{
. = 0x1000;
.text : ALIGN(4K) { *(.text*) } :text
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
.data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data
}
+339
View File
@@ -0,0 +1,339 @@
/* Deshab ping — 终端风格 ICMP Echo 工具 stub
* 命令行输入 IP 地址,发送 ICMP Echo Request。
* 当前为 stub,提示需要 TCP/IP 栈支持。
* Esc 键退出返回 DSK。
*/
#include "../../firstInit/ascii_bitmaps.c"
#include "../desktop_app.h"
/* ---- 终端参数 ---- */
#define TERM_COLS 120
#define TERM_ROWS 40
#define TERM_MAX_CHARS (TERM_COLS * TERM_ROWS)
#define CHAR_STEP 12
#define CHAR_H 18
#define PROMPT_STR "ping> "
static da_app_context g_ac;
/* 终端缓冲区 */
static u8 term_ch[TERM_MAX_CHARS];
static u32 term_fg[TERM_MAX_CHARS];
static int term_w = TERM_COLS;
static int term_h = TERM_ROWS;
static int cur_col = 0;
static int cur_row = 0;
/* 输入缓冲 */
static char input_buf[256];
static int input_len = 0;
static int input_cursor = 0;
/* 命令历史 */
#define HIST_SIZE 16
static char history[HIST_SIZE][256];
static int hist_count = 0;
static int hist_idx = 0;
/* 坐标 */
static int term_x, term_y;
static void term_init_layout(void) {
term_x = 8;
term_y = DA_TITLEBAR_H + 4;
int avail_w = (int)g_ac.fb_w - 16;
int avail_h = (int)g_ac.fb_h - term_y - DA_STATUSBAR_H - 8;
term_w = avail_w / CHAR_STEP;
term_h = avail_h / CHAR_H;
if (term_w < 10) term_w = 10;
if (term_h < 5) term_h = 5;
if (term_w > TERM_COLS) term_w = TERM_COLS;
if (term_h > TERM_ROWS) term_h = TERM_ROWS;
}
static void term_clear(void) {
for (int i = 0; i < TERM_MAX_CHARS; i++) {
term_ch[i] = ' ';
term_fg[i] = DA_TEXT_PRIMARY;
}
cur_col = 0; cur_row = 0;
}
static void term_scroll(void) {
for (int r = 1; r < term_h; r++) {
for (int c = 0; c < term_w; c++) {
term_ch[(r-1)*TERM_COLS+c] = term_ch[r*TERM_COLS+c];
term_fg[(r-1)*TERM_COLS+c] = term_fg[r*TERM_COLS+c];
}
}
for (int c = 0; c < term_w; c++) {
term_ch[(term_h-1)*TERM_COLS+c] = ' ';
term_fg[(term_h-1)*TERM_COLS+c] = DA_TEXT_PRIMARY;
}
cur_row = term_h - 1; cur_col = 0;
}
static void term_newline(void) {
cur_col = 0; cur_row++;
if (cur_row >= term_h) term_scroll();
}
static void term_putc_color(char c, u32 color) {
if (c == '\n') { term_newline(); return; }
if (c == '\r') { cur_col = 0; return; }
if (c == '\t') { int sp = 4 - (cur_col % 4); for (int i = 0; i < sp; i++) term_putc_color(' ', color); return; }
if (c < ' ' || c > '~') c = '?';
if (cur_col >= term_w) term_newline();
int idx = cur_row * TERM_COLS + cur_col;
term_ch[idx] = (u8)c; term_fg[idx] = color;
cur_col++;
}
static void term_putc(char c) { term_putc_color(c, DA_TEXT_PRIMARY); }
static void term_puts_color(const char *s, u32 color) { while (*s) term_putc_color(*s++, color); }
static void term_puts(const char *s) { term_puts_color(s, DA_TEXT_PRIMARY); }
/* ---- 绘制 ---- */
static void redraw_all(void) {
da_fill_bg(&g_ac, DA_BG_PRIMARY);
da_draw_titlebar(&g_ac, "Ping", (i64)g_ac.fb_w);
/* 终端字符 */
for (int r = 0; r < term_h; r++) {
for (int c = 0; c < term_w; c++) {
int idx = r * TERM_COLS + c;
if (term_ch[idx] == ' ') continue;
da_draw_char(&g_ac, term_ch[idx],
term_x + c * CHAR_STEP, term_y + r * CHAR_H,
term_fg[idx], DA_BG_PRIMARY);
}
}
/* 光标 */
if (cur_col < term_w && cur_row < term_h) {
da_fill_rect(&g_ac, term_x + cur_col * CHAR_STEP,
term_y + cur_row * CHAR_H + CHAR_H - 3, 11, 2, DA_ACCENT);
}
/* 状态栏 */
char status[64];
int p = 0;
const char *s1 = "ICMP Echo | Esc 退出 ";
while (*s1) status[p++] = *s1++;
u8 hh, mm; da_rtc_time(&hh, &mm);
status[p++] = '0' + hh/10; status[p++] = '0' + hh%10;
status[p++] = ':'; status[p++] = '0' + mm/10; status[p++] = '0' + mm%10;
status[p] = 0;
da_draw_statusbar(&g_ac, status, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
}
/* ---- IP 格式校验 ---- */
static int is_valid_ip(const char *s) {
int dots = 0, digits = 0;
while (*s) {
if (*s == '.') { dots++; digits = 0; }
else if (*s >= '0' && *s <= '9') { digits++; if (digits > 3) return 0; }
else return 0;
s++;
}
return dots == 3;
}
/* ---- Ping 模拟 ---- */
static void do_ping(const char *ip) {
term_puts_color("PING ", DA_PROMPT_COLOR);
term_puts_color(ip, DA_ACCENT_LIGHT);
term_puts_color(" 56(84) bytes of data.\n", DA_TEXT_DIM);
/* stub: 检查 kernel_api 是否有 net 接口 */
if (!g_ac.kernel_api) {
term_puts_color(" [错误] 无内核 API\n", DA_ERROR);
return;
}
u64 net_api = *(u64 *)((u64)g_ac.kernel_api + 0xC0);
if (!net_api) {
term_puts_color(" [错误] 需要 TCP/IP 栈支持\n", DA_ERROR);
term_puts_color(" 当前内核未实现 ICMP 协议栈。\n", DA_WARNING);
term_puts_color(" 请等待后续版本更新。\n", DA_TEXT_DIM);
term_putc('\n');
term_puts_color("--- ", DA_TEXT_DIM);
term_puts_color(ip, DA_TEXT_DIM);
term_puts_color(" ping statistics ---\n", DA_TEXT_DIM);
term_puts_color(" 0 packets transmitted, 0 received, 100% packet loss\n", DA_ERROR);
return;
}
/* 如果有 net API,此处应实现真正的 ICMP Echo */
term_puts_color(" [stub] 网络功能尚未实现\n", DA_WARNING);
}
/* ---- 内置命令 ---- */
static int str_eq(const char *a, const char *b) {
while (*a && *b) { if (*a != *b) return 0; a++; b++; }
return *a == *b;
}
static void cmd_help(void) {
term_puts_color("ping — ICMP Echo 请求工具\n", DA_PROMPT_COLOR);
term_puts(" <IP> 发送 ICMP Echo Request\n");
term_puts(" help 显示此帮助\n");
term_puts(" clear 清屏\n");
term_puts(" version 显示版本\n");
term_puts(" Esc 退出\n");
}
static void cmd_clear(void) { term_clear(); }
static void cmd_version(void) {
term_puts_color("Deshab ping v0.1 (stub)\n", DA_SUCCESS);
term_puts_color(" ICMP Echo 功能需要 TCP/IP 栈支持\n", DA_TEXT_DIM);
}
static void execute_command(const char *cmd) {
while (*cmd == ' ') cmd++;
if (*cmd == 0) return;
if (str_eq(cmd, "help") || str_eq(cmd, "?")) { cmd_help(); return; }
if (str_eq(cmd, "clear") || str_eq(cmd, "cls")) { cmd_clear(); return; }
if (str_eq(cmd, "version") || str_eq(cmd, "ver")) { cmd_version(); return; }
/* 非内置命令视为 IP 地址 */
if (is_valid_ip(cmd)) {
do_ping(cmd);
} else {
term_puts_color("无效 IP 地址: ", DA_ERROR);
term_puts_color(cmd, DA_ERROR);
term_putc('\n');
term_puts_color("请输入有效的 IPv4 地址 (如 192.168.1.1)\n", DA_TEXT_DIM);
}
}
static void add_history(const char *cmd) {
if (hist_count < HIST_SIZE) {
int i;
for (i = 0; cmd[i] && i < 255; i++) history[hist_count][i] = cmd[i];
history[hist_count][i] = 0;
hist_count++;
}
hist_idx = hist_count;
}
/* ---- 输入行重绘 ---- */
static void redraw_input_line(void) {
int plen = 0;
while (PROMPT_STR[plen]) plen++;
i64 y = term_y + (i64)cur_row * CHAR_H;
da_fill_rect(&g_ac, term_x, y, (i64)term_w * CHAR_STEP, CHAR_H, DA_BG_PRIMARY);
for (int i = 0; i < plen; i++)
da_draw_char(&g_ac, PROMPT_STR[i], term_x + i * CHAR_STEP, y, DA_PROMPT_COLOR, DA_BG_PRIMARY);
for (int i = 0; i < input_len; i++)
da_draw_char(&g_ac, input_buf[i], term_x + (plen + i) * CHAR_STEP, y, DA_TEXT_PRIMARY, DA_BG_PRIMARY);
cur_col = plen + input_cursor;
da_fill_rect(&g_ac, term_x + cur_col * CHAR_STEP, y + CHAR_H - 3, 11, 2, DA_ACCENT);
}
/* ---- 主入口 ---- */
__attribute__((visibility("default")))
void dsk_entry(const da_boot_context *ctx) {
__asm__ volatile("cli");
da_slog("ping", "boot");
if (!ctx || ctx->magic != DA_BOOT_MAGIC) {
da_slog("ping", "bad context");
for(;;) __asm__("hlt");
}
da_init(&g_ac, ctx);
term_init_layout();
term_clear();
/* 启动 banner */
term_puts_color("=== Deshab ping v0.1 (stub) ===\n", DA_ACCENT);
term_puts_color("ICMP Echo 请求工具\n", DA_SUCCESS);
term_puts_color("输入 IP 地址发送 ping | 输入 'help' 查看帮助 | Esc 退出\n", DA_TEXT_DIM);
term_puts_color("注意: 当前需要 TCP/IP 栈支持\n", DA_WARNING);
term_putc('\n');
redraw_all();
int shift = 0, e0 = 0;
for (;;) {
term_puts_color(PROMPT_STR, DA_PROMPT_COLOR);
input_len = 0; input_cursor = 0; input_buf[0] = 0;
int plen = 0; while (PROMPT_STR[plen]) plen++;
cur_col = plen;
redraw_all();
int cmd_done = 0;
while (!cmd_done) {
u8 st = inb(0x64);
if (!(st & 1)) { __asm__("pause"); continue; }
u8 data = inb(0x60);
if (st & 0x20) continue;
u8 sc = data;
if (sc == 0xE0) { e0 = 1; continue; }
if (sc == 0x2A || sc == 0x36) { shift = 1; continue; }
if (sc == 0xAA || sc == 0xB6) { shift = 0; continue; }
if (sc & 0x80) { e0 = 0; continue; }
/* Esc 退出 */
if (sc == 0x01 && !e0) {
da_slog("ping", "exit");
return;
}
if (e0) {
if (sc == 0x4B) { if (input_cursor > 0) input_cursor--; }
else if (sc == 0x4D) { if (input_cursor < input_len) input_cursor++; }
else if (sc == 0x47) { input_cursor = 0; }
else if (sc == 0x4F) { input_cursor = input_len; }
else if (sc == 0x48) {
if (hist_idx > 0) {
hist_idx--;
int i; for (i = 0; history[hist_idx][i] && i < 255; i++) input_buf[i] = history[hist_idx][i];
input_len = i; input_cursor = i; input_buf[i] = 0;
}
}
else if (sc == 0x50) {
if (hist_idx < hist_count - 1) {
hist_idx++;
int i; for (i = 0; history[hist_idx][i] && i < 255; i++) input_buf[i] = history[hist_idx][i];
input_len = i; input_cursor = i; input_buf[i] = 0;
} else if (hist_idx < hist_count) {
hist_idx = hist_count;
input_len = 0; input_cursor = 0; input_buf[0] = 0;
}
}
e0 = 0;
redraw_input_line();
continue;
}
if (sc == 0x1C) { /* Enter */
input_buf[input_len] = 0;
term_putc('\n');
if (input_len > 0) add_history(input_buf);
execute_command(input_buf);
cmd_done = 1;
continue;
}
if (sc == 0x0E) { /* Backspace */
if (input_cursor > 0) {
for (int i = input_cursor - 1; i < input_len - 1; i++)
input_buf[i] = input_buf[i+1];
input_len--; input_cursor--;
input_buf[input_len] = 0;
}
redraw_input_line();
continue;
}
char c = da_scan_to_ascii(sc, shift);
if (c && c >= 32 && c <= 126 && input_len < 255) {
for (int i = input_len; i > input_cursor; i--)
input_buf[i] = input_buf[i-1];
input_buf[input_cursor++] = c;
input_len++;
redraw_input_line();
}
}
redraw_all();
}
}