Files
2026-08-05 13:58:13 +08:00

377 lines
14 KiB
C
Raw Permalink 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.
/* 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 <SDL2/SDL.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdint.h>
#include <utsm/dsk.h>
/* ---- 全局缓冲(供 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;
}