Files
Deshab-OS/CODE/tools/desktop_app.h
T
2026-07-28 12:00:02 +08:00

383 lines
15 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 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 设备函数类型(与 shell/fat32_io.h 一致:u32 index 固定 0) */
typedef int (*da_block_read_fn)(u32 index, u64 lba, u32 count, void *buffer);
typedef int (*da_block_write_fn)(u32 index, u64 lba, u32 count, const 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;
da_block_write_fn block_write;
const void *kernel_api;
} da_app_context;
/* 从 dsk_boot_context 初始化 da_app_context。
* block API 从 kernel_api + 0xA8 获取:read @ +0x10,write @ +0x18。 */
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;
ac->block_read = 0;
ac->block_write = 0;
u64 api = ctx->dkm_kernel_api;
u64 blk_api = *(u64 *)(api + 0xA8);
if (blk_api) {
ac->block_read = (da_block_read_fn)*(u64 *)(blk_api + 0x10);
ac->block_write = (da_block_write_fn)*(u64 *)(blk_api + 0x18);
}
}
/* ========== 帧缓冲绘制 ========== */
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;
/* PS/2 鼠标包:buf[1]/buf[2] 已是 9 位补码的低 8 位,
* (signed char) 强转即可正确符号扩展,无需再依据 buf[0] 的 sign bit 反转。
* X 直接相加;Y 需翻转一次(PS/2 Y 向上为正,屏幕 Y 向下为正)。
* 与 desktop/main.c:ps2_mouse_poll 保持一致。 */
int dx = (int)(signed char)m->buf[1];
int dy = (int)(signed char)m->buf[2];
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 */