/* host_main.c — Deshab 桌面 Linux SDL2 宿主 * * 仅在 -DDESHAB_HOST 编译时链接。提供: * - SDL2 窗口 + streaming texture(模拟 framebuffer,g_host_backbuf) * - 鼠标/键盘事件队列(模拟 PS/2,供 host_mouse_poll/host_kb_poll 消费) * - deshab.img GPT ESP 文件 block 后端(模拟 kernel_api block provider) * - 单调时钟 / 当天秒数(替代 rdtsc / RTC CMOS) * * main() 构造 dsk_boot_context 后调用 dsk_entry(),进入桌面主循环。 * 主循环每帧末尾调用 host_pump_and_present() 上传 back buffer 并 pump 事件。 */ #include "host_main.h" #include #include #include #include #include #include #include #include #include /* ---- 全局缓冲(供 desktop/main.c 经 extern 使用) ---- */ u8 *g_host_sprite = NULL; u8 *g_host_backbuf = NULL; /* ---- SDL 状态 ---- */ static SDL_Window *g_win = NULL; static SDL_Renderer *g_rend = NULL; static SDL_Texture *g_tex = NULL; static int g_fb_w = 1280, g_fb_h = 720; /* ---- 鼠标状态(SDL 事件累积,host_mouse_poll 消费) ---- */ static struct { int x, y; /* 绝对坐标 */ int btn; /* bit0=左 bit1=右 */ int prev_btn; /* 上次 poll 时的按键状态 */ int changed; /* 自上次 poll 以来有变化 */ } g_mse = { 400, 300, 0, 0, 0 }; /* ---- 键盘扫描码队列(SDL 键事件 → set-1 字节序列) ---- */ #define KB_QSZ 64 static u8 g_kb_q[KB_QSZ]; static int g_kb_head = 0, g_kb_tail = 0; static void kb_push(u8 b) { int nxt = (g_kb_tail + 1) % KB_QSZ; if (nxt == g_kb_head) return; /* 满,丢弃 */ g_kb_q[g_kb_tail] = b; g_kb_tail = nxt; } /* ---- 日志 ---- */ void host_log(const char *s) { fputs(s, stderr); fflush(stderr); } void host_log_char(char c) { fputc(c, stderr); fflush(stderr); } /* ---- 时间 ---- */ u64 host_now_us(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (u64)ts.tv_sec * 1000000ULL + (u64)ts.tv_nsec / 1000ULL; } int host_rtc_seconds(void) { time_t t = time(NULL); struct tm *lt = localtime(&t); return lt->tm_hour * 3600 + lt->tm_min * 60 + lt->tm_sec; } /* ---- 鼠标 ---- */ int host_mouse_poll(int *px, int *py, int *pbtn, int *p_left_pressed, int *p_left_released, int *p_right_pressed, int max_x, int max_y) { if (!g_mse.changed && g_mse.btn == g_mse.prev_btn) return 0; g_mse.changed = 0; int btn = g_mse.btn; int prev = g_mse.prev_btn; g_mse.prev_btn = btn; int x = g_mse.x, y = g_mse.y; if (x < 0) x = 0; if (y < 0) y = 0; if (x > max_x) x = max_x; if (y > max_y) y = max_y; *px = x; *py = y; *pbtn = btn; *p_left_pressed = (btn & 1) && !(prev & 1); *p_left_released = !(btn & 1) && (prev & 1); *p_right_pressed = (btn & 2) && !(prev & 2); return 1; } /* ---- 键盘 ---- */ int host_kb_poll(u8 *out_sc) { if (g_kb_head == g_kb_tail) return 0; *out_sc = g_kb_q[g_kb_head]; g_kb_head = (g_kb_head + 1) % KB_QSZ; return 1; } /* SDL_Scancode → PS/2 set-1 make 码(单字节;E0 前缀键返回 0xFF 表示需 E0) */ static u8 sdl_to_set1(SDL_Scancode sc, int *e0) { *e0 = 0; switch (sc) { case SDL_SCANCODE_A: return 0x1E; case SDL_SCANCODE_B: return 0x30; case SDL_SCANCODE_C: return 0x2E; case SDL_SCANCODE_D: return 0x20; case SDL_SCANCODE_E: return 0x12; case SDL_SCANCODE_F: return 0x21; case SDL_SCANCODE_G: return 0x22; case SDL_SCANCODE_H: return 0x23; case SDL_SCANCODE_I: return 0x17; case SDL_SCANCODE_J: return 0x24; case SDL_SCANCODE_K: return 0x25; case SDL_SCANCODE_L: return 0x26; case SDL_SCANCODE_M: return 0x32; case SDL_SCANCODE_N: return 0x31; case SDL_SCANCODE_O: return 0x18; case SDL_SCANCODE_P: return 0x19; case SDL_SCANCODE_Q: return 0x10; case SDL_SCANCODE_R: return 0x13; case SDL_SCANCODE_S: return 0x1F; case SDL_SCANCODE_T: return 0x14; case SDL_SCANCODE_U: return 0x16; case SDL_SCANCODE_V: return 0x2F; case SDL_SCANCODE_W: return 0x11; case SDL_SCANCODE_X: return 0x2D; case SDL_SCANCODE_Y: return 0x15; case SDL_SCANCODE_Z: return 0x2C; case SDL_SCANCODE_1: return 0x02; case SDL_SCANCODE_2: return 0x03; case SDL_SCANCODE_3: return 0x04; case SDL_SCANCODE_4: return 0x05; case SDL_SCANCODE_5: return 0x06; case SDL_SCANCODE_6: return 0x07; case SDL_SCANCODE_7: return 0x08; case SDL_SCANCODE_8: return 0x09; case SDL_SCANCODE_9: return 0x0A; case SDL_SCANCODE_0: return 0x0B; case SDL_SCANCODE_RETURN: return 0x1C; case SDL_SCANCODE_BACKSPACE: return 0x0E; case SDL_SCANCODE_TAB: return 0x0F; case SDL_SCANCODE_SPACE: return 0x39; case SDL_SCANCODE_ESCAPE: return 0x01; case SDL_SCANCODE_LSHIFT: return 0x2A; case SDL_SCANCODE_RSHIFT: return 0x36; case SDL_SCANCODE_LCTRL: return 0x1D; case SDL_SCANCODE_RCTRL: return 0x1D; case SDL_SCANCODE_LALT: return 0x38; case SDL_SCANCODE_RALT: return 0x38; case SDL_SCANCODE_CAPSLOCK: return 0x3A; case SDL_SCANCODE_MINUS: return 0x0C; case SDL_SCANCODE_EQUALS: return 0x0D; case SDL_SCANCODE_LEFTBRACKET: return 0x1A; case SDL_SCANCODE_RIGHTBRACKET: return 0x1B; case SDL_SCANCODE_SEMICOLON: return 0x27; case SDL_SCANCODE_APOSTROPHE: return 0x28; case SDL_SCANCODE_GRAVE: return 0x29; case SDL_SCANCODE_BACKSLASH: return 0x2B; case SDL_SCANCODE_COMMA: return 0x33; case SDL_SCANCODE_PERIOD: return 0x34; case SDL_SCANCODE_SLASH: return 0x35; case SDL_SCANCODE_LEFT: *e0 = 1; return 0x4B; case SDL_SCANCODE_RIGHT: *e0 = 1; return 0x4D; case SDL_SCANCODE_UP: *e0 = 1; return 0x48; case SDL_SCANCODE_DOWN: *e0 = 1; return 0x50; default: return 0; } } static void handle_key_event(SDL_Scancode sc, int down) { int e0 = 0; u8 code = sdl_to_set1(sc, &e0); if (!code) return; if (e0) kb_push(0xE0); kb_push(down ? code : (u8)(code | 0x80)); } /* ---- 帧末 pump + present ---- */ int host_pump_and_present(void) { int quit = 0; /* 上传 back buffer 到 SDL texture 并 present */ if (g_tex && g_host_backbuf) { SDL_UpdateTexture(g_tex, NULL, g_host_backbuf, g_fb_w * 4); SDL_RenderClear(g_rend); SDL_RenderCopy(g_rend, g_tex, NULL, NULL); SDL_RenderPresent(g_rend); } /* pump 事件 */ SDL_Event ev; while (SDL_PollEvent(&ev)) { switch (ev.type) { case SDL_QUIT: quit = 1; break; case SDL_MOUSEMOTION: g_mse.x = ev.motion.x; g_mse.y = ev.motion.y; g_mse.changed = 1; break; case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: { int down = (ev.type == SDL_MOUSEBUTTONDOWN); if (ev.button.button == SDL_BUTTON_LEFT) { if (down) g_mse.btn |= 1; else g_mse.btn &= ~1; g_mse.changed = 1; } else if (ev.button.button == SDL_BUTTON_RIGHT) { if (down) g_mse.btn |= 2; else g_mse.btn &= ~2; g_mse.changed = 1; } break; } case SDL_KEYDOWN: case SDL_KEYUP: { /* 忽略重复事件(SDL_KEYDOWN 带 repeat>0) */ if (ev.key.repeat) break; handle_key_event(ev.key.keysym.scancode, ev.type == SDL_KEYDOWN); break; } } } return quit; } /* ---- block 后端:deshab.img GPT ESP 文件 ---- */ static int g_img_fd = -1; static u64 g_esp_byte_off = 0; /* ESP 分区起始字节偏移 */ /* 读取 img LBA1 GPT header,定位 ESP 分区起始 LBA。失败回退 2048。 */ static u64 find_esp_offset(int fd) { u8 hdr[512]; if (pread(fd, hdr, 512, 512) != 512) return 2048 * 512; /* GPT signature "EFI PART" */ if (memcmp(hdr, "EFI PART", 8) != 0) return 2048 * 512; u64 entries_lba = 0; u32 entry_count = 0, entry_size = 0; memcpy(&entries_lba, hdr + 72, 8); memcpy(&entry_count, hdr + 80, 4); memcpy(&entry_size, hdr + 84, 4); if (entry_size < 56 || entry_count == 0 || entry_count > 256) return 2048 * 512; /* ESP 类型 GUID(混合字节序):c12a7328-f81f-11d2-ba4b-00a0c93ec93b */ static const u8 esp_guid[16] = { 0x28,0x73,0x2a,0xc1, 0x1f,0xf8, 0xd2,0x11, 0xba,0x4b, 0x00,0xa0,0xc9,0x3e,0xc9,0x3b }; u8 *entries = (u8 *)malloc((u64)entry_count * entry_size); if (!entries) return 2048 * 512; u64 entries_byte = entries_lba * 512; ssize_t got = pread(fd, entries, (u64)entry_count * entry_size, entries_byte); if (got <= 0) { free(entries); return 2048 * 512; } u64 esp_lba = 0; int found = 0; for (u32 i = 0; i < entry_count; i++) { u8 *e = entries + (u64)i * entry_size; if (memcmp(e, esp_guid, 16) == 0) { memcpy(&esp_lba, e + 32, 8); found = 1; break; } } free(entries); return found ? (esp_lba * 512) : (2048 * 512); } /* block_read/write:desktop 期望 int (u32 index, u64 lba, u32 count, void* buf) * index 固定 0;扇区 512 字节;偏移 = esp_byte_off + lba*512。 */ static int host_block_read(u32 index, u64 lba, u32 count, void *buf) { (void)index; if (g_img_fd < 0) return -1; u64 off = g_esp_byte_off + lba * 512ULL; u64 total = (u64)count * 512ULL; u8 *p = (u8 *)buf; while (total > 0) { ssize_t n = pread(g_img_fd, p, total, off); if (n <= 0) return -1; p += n; off += (u64)n; total -= (u64)n; } return 0; } static int host_block_write(u32 index, u64 lba, u32 count, const void *buf) { (void)index; if (g_img_fd < 0) return -1; u64 off = g_esp_byte_off + lba * 512ULL; u64 total = (u64)count * 512ULL; const u8 *p = (const u8 *)buf; while (total > 0) { ssize_t n = pwrite(g_img_fd, p, total, off); if (n <= 0) return -1; p += n; off += (u64)n; total -= (u64)n; } return 0; } /* ---- 伪造 kernel_api:让 dsk_entry 现有 block 获取路径零改动 ---- * desktop/main.c: u64 blk = *(u64*)(ctx->dkm_kernel_api + 0xA8); * g_block_read = *(u64*)(blk + 16); * g_block_write = *(u64*)(blk + 24); * 故 host_kernel_api 偏移 0xA8 处填 &host_block_api; * host_block_api[2] = read, [3] = write(偏移 16/24)。 */ static u64 host_block_api[8]; static u8 host_kernel_api[0xB0 + 8]; static void build_fake_kernel_api(void) { memset(host_kernel_api, 0, sizeof(host_kernel_api)); memset(host_block_api, 0, sizeof(host_block_api)); host_block_api[2] = (u64)(uintptr_t)host_block_read; host_block_api[3] = (u64)(uintptr_t)host_block_write; *(u64 *)(host_kernel_api + 0xA8) = (u64)(uintptr_t)host_block_api; } /* ---- main ---- */ int main(int argc, char **argv) { const char *img_path = "../../ISO/deshab.img"; int width = 1280, height = 720; for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "--img") == 0 && i + 1 < argc) { img_path = argv[++i]; continue; } if (strcmp(argv[i], "--width") == 0 && i + 1 < argc) { width = atoi(argv[++i]); continue; } if (strcmp(argv[i], "--height") == 0 && i + 1 < argc) { height = atoi(argv[++i]); continue; } if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { fprintf(stderr, "usage: desktop_host [--img PATH] [--width W] [--height H]\n"); return 0; } } g_fb_w = width; g_fb_h = height; /* 打开镜像 */ g_img_fd = open(img_path, O_RDWR); if (g_img_fd < 0) { /* 只读回退 */ g_img_fd = open(img_path, O_RDONLY); if (g_img_fd < 0) { fprintf(stderr, "[host] cannot open %s: ", img_path); perror(""); return 1; } fprintf(stderr, "[host] warning: %s opened read-only, FAT32 writes will fail\n", img_path); } g_esp_byte_off = find_esp_offset(g_img_fd); fprintf(stderr, "[host] img=%s esp_off=%llu width=%d height=%d\n", img_path, (unsigned long long)g_esp_byte_off, width, height); /* SDL */ if (SDL_Init(SDL_INIT_VIDEO) != 0) { fprintf(stderr, "[host] SDL_Init: %s\n", SDL_GetError()); return 1; } g_win = SDL_CreateWindow("Deshab Desktop (Linux host)", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_SHOWN); if (!g_win) { fprintf(stderr, "[host] SDL_CreateWindow: %s\n", SDL_GetError()); return 1; } g_rend = SDL_CreateRenderer(g_win, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); if (!g_rend) g_rend = SDL_CreateRenderer(g_win, -1, 0); if (!g_rend) { fprintf(stderr, "[host] SDL_CreateRenderer: %s\n", SDL_GetError()); return 1; } g_tex = SDL_CreateTexture(g_rend, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height); if (!g_tex) { fprintf(stderr, "[host] SDL_CreateTexture: %s\n", SDL_GetError()); return 1; } /* back buffer + sprite buffer */ size_t fbsz = (size_t)width * height * 4; g_host_backbuf = (u8 *)malloc(fbsz); g_host_sprite = (u8 *)malloc(fbsz); if (!g_host_backbuf || !g_host_sprite) { fprintf(stderr, "[host] alloc buffer failed\n"); return 1; } memset(g_host_backbuf, 0, fbsz); memset(g_host_sprite, 0, fbsz); build_fake_kernel_api(); /* 构造 dsk_boot_context */ dsk_boot_context ctx; memset(&ctx, 0, sizeof(ctx)); ctx.magic = DSK_BOOT_MAGIC; ctx.abi_version = DSK_BOOT_ABI_VERSION; ctx.size = sizeof(ctx); ctx.framebuffer_address = (u64)(uintptr_t)g_host_backbuf; ctx.framebuffer_width = (u64)width; ctx.framebuffer_height = (u64)height; ctx.framebuffer_pitch = (u64)width * 4; ctx.framebuffer_bpp = 32; ctx.dkm_kernel_api = (u64)(uintptr_t)host_kernel_api; /* reserved[5]=0:无 linux_compat 服务,桌面侧 g_lxc_svc 保持 NULL */ fprintf(stderr, "[host] entering dsk_entry\n"); dsk_entry(&ctx); fprintf(stderr, "[host] dsk_entry returned, exiting\n"); SDL_DestroyTexture(g_tex); SDL_DestroyRenderer(g_rend); SDL_DestroyWindow(g_win); SDL_Quit(); if (g_img_fd >= 0) close(g_img_fd); free(g_host_backbuf); free(g_host_sprite); return 0; }