/* 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 */