feat: Phase 1 网络端到端打通 + FirstInit/Login/DevMode 完善

- 用户态共享网络协议栈 CODE/tools/net_stack.h(eth/ARP/IPv4/ICMP/UDP-DNS/TCP static inline,轮询模型)
- shell 内建 ping/curl/run 命令;ping.elf/curl.elf 对齐 net_stack.h 真实协议栈
- shell dev_mode 串口镜像(g_serial_mirror)便于自动化验证
- DSK 启动流程:firstInit.txt 双行格式(firstInit + dev_mode),dev_mode=1 触发 shell 自动回归测试
- FirstInit 完成→持久化 USER.CONF + 翻转 firstInit 0->1(保留 dev_mode)
- firstInit=1 时 DSK 加载 login.elf 进行用户选择 + 密码校验后再进 desktop
- 新增 CODE/login/ 登录应用、CODE/DKM/dkm_shared.h、Logo.png
- ahci/e1000/virtio_net/fat32/console_fb 驱动更新;utsm serial/block/dsk_loader 调整
- build.ps1 打包 ping.elf/curl.elf 进 SATA FAT32 根目录
- 移除 CODE/tools/bash(与 shell.elf 重复)
This commit is contained in:
xyou
2026-07-26 21:07:36 +08:00
parent 48dcb5568e
commit efa626ed17
83 changed files with 4993 additions and 1785 deletions
+559 -143
View File
@@ -16,6 +16,42 @@ typedef long long i64;
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; }
/* ---- TSC-based timing (实机要求: 用 CPU 频率计算, 不用循环) ---- */
static u64 g_tsc_per_ms = 0;
static __inline__ u64 rdtsc_fi(void) {
u32 lo, hi;
__asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi));
return ((u64)hi << 32) | lo;
}
/* 用 PIT (8254, 1.193182 MHz) 校准 TSC 频率 */
static void tsc_calibrate_fi(void) {
outb(0x43, 0x30); /* ch0, lo+hi, mode 0, binary */
outb(0x40, 0x7c); /* 11932 low = ~10ms */
outb(0x40, 0x2e); /* 11932 high */
u64 tsc_start = rdtsc_fi();
u16 prev = 0; u64 loops = 0;
for (;;) {
outb(0x43, 0x00);
u16 cur = (u16)inb(0x40) | ((u16)inb(0x40) << 8);
if (cur > prev && loops > 10) break;
prev = cur; loops++;
}
u64 tsc_end = rdtsc_fi();
g_tsc_per_ms = (tsc_end - tsc_start) / 10;
}
static void delay_ms_fi(u32 ms) {
if (!g_tsc_per_ms) {
for (volatile u32 i = 0; i < 100000 * ms; i++) __asm__ volatile("pause");
return;
}
u64 target = g_tsc_per_ms * ms;
u64 start = rdtsc_fi();
while (rdtsc_fi() - start < target) __asm__ volatile("pause");
}
static void sputc(char c) {
for (unsigned int i=0; i<100000; i++) { if (inb(COM1+5)&0x20) break; }
outb(COM1, (unsigned char)c);
@@ -23,16 +59,20 @@ 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"); }
/* ---- PS/2 controller / mouse helpers (timeout-protected) ---- */
/* ---- PS/2 controller / mouse helpers (timeout-protected, TSC-based) ---- */
static int ps2_wait_write(void) {
for (volatile u32 i = 0; i < 100000; i++) {
u64 deadline = rdtsc_fi() + g_tsc_per_ms * 100; /* 100ms 超时 */
while (rdtsc_fi() < deadline) {
if (!(inb(0x64) & 0x02)) return 0; /* input buffer empty */
__asm__ volatile("pause");
}
return -1;
}
static int ps2_wait_read(void) {
for (volatile u32 i = 0; i < 100000; i++) {
u64 deadline = rdtsc_fi() + g_tsc_per_ms * 100; /* 100ms 超时 */
while (rdtsc_fi() < deadline) {
if (inb(0x64) & 0x01) return 0; /* output buffer full */
__asm__ volatile("pause");
}
return -1;
}
@@ -81,12 +121,12 @@ static void mouse_init(void) {
logl("[mouse] init done");
}
#define BG_TOP 0xFFC8E0F0u
#define BG_BOTTOM 0xFF49306Fu
#define BG_MID 0xFF8888B0u
#define CARD_BG 0xFFEAF5FBu
#define INPUT_BG 0xFFF0F5FAu
#define TEXT_FG 0xFF5F7FA6u
#define BG_TOP 0xFF2D2D30u
#define BG_BOTTOM 0xFF1A1A1Eu
#define BG_MID 0xFF3C3C40u
#define CARD_BG 0xFF353539u
#define INPUT_BG 0xFF2A2A2Eu
#define TEXT_FG 0xFFE0E0E0u
#define WELCOME_FG 0xFFE8F2FCu
static u64 fb_a, fb_w, fb_h, fb_p;
@@ -127,6 +167,38 @@ static void fb_text(u32 *fb, const char *s, i64 x, i64 y, u32 fg, u32 bg) {
for (i64 i=0; s[i]; i++) fb_char(fb, (u32)(u8)s[i], x+i*ASCII_STEP, y, fg, bg);
}
/* width of an ASCII string in pixels (used to center English subtitles) */
static i64 fb_text_width(const char *s) {
i64 n = 0;
while (s[n]) n++;
return n * ASCII_STEP;
}
/* Alpha-blended ASCII text — same as fb_text but multiplied by a global alpha,
* used to fade English subtitles in/out alongside the Chinese bitmaps. */
static void fb_text_alpha(u32 *fb, const char *s, i64 x, i64 y, u32 fg, u32 bg, u32 global_alpha) {
for (i64 i=0; s[i]; i++) {
u32 ch = (u32)(u8)s[i];
if (ch < ' ' || ch > '~') ch = ' ';
u32 idx = 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;
u32 eff_a = ((u32)a * global_alpha) >> 8;
if (eff_a == 0) continue;
i64 xx = x + i*ASCII_STEP + c;
if (xx < 0 || (u64)xx >= fb_w) continue;
line[(u64)xx] = (eff_a == 255) ? fg : blend(bg, fg, eff_a);
}
}
}
}
/* draw pre-rendered grayscale bitmap – writes only, no framebuffer read */
static void fb_bitmap_alpha(u32 *fb, const u8 *data, i64 w, i64 h, i64 x, i64 y, u32 fg, u32 bg, u32 global_alpha) {
for (i64 r=0; r<h; r++) {
@@ -136,7 +208,9 @@ static void fb_bitmap_alpha(u32 *fb, const u8 *data, i64 w, i64 h, i64 x, i64 y,
for (i64 c=0; c<w; c++) {
i64 xx = x + c;
if (xx < 0 || (u64)xx >= fb_w) continue;
u32 a = ((u32)data[r*(u64)w + (u64)c] * global_alpha) >> 8;
u8 val = data[r*(u64)w + (u64)c];
if (val == 0) continue; /* skip transparent – let background show through */
u32 a = ((u32)val * global_alpha) >> 8;
line[(u64)xx] = blend(bg, fg, a);
}
}
@@ -228,25 +302,21 @@ static void stroke_rounded_rect(u32 *fb, i64 x, i64 y, i64 w, i64 h, i64 radius,
}
static void delay_frame(void) {
for (volatile u32 d=0; d<300000; d++) __asm__("pause");
delay_ms_fi(33); /* 实机: 基于 TSC 的 33ms 帧间隔 (~30fps) */
}
#include "text_bitmaps.c"
static void fade_ring(u32 *fb, i64 cx, i64 cy, u32 bg) {
/* Smoothly erase a square sprite area with the same vertical gradient as DSK. */
/* Erase the spinner sprite area with gradient background in one frame. */
(void)bg;
i64 r = 72;
for (int step=0; step<12; step++) {
(void)step;
fill_gradient_rect(fb, cx-r, cy-r, r*2+1, r*2+1);
delay_frame();
}
fill_gradient_rect(fb, cx-r, cy-r, r*2+1, r*2+1);
}
static void draw_card(u32 *fb, i64 x, i64 y, i64 w, i64 h, u32 card, u32 bg_color) {
(void)bg_color;
fill_rounded_rect(fb, x, y, w, h, 12, card);
fill_rounded_rect(fb, x, y, w, h, 4, card);
}
/* Draw gradient background + card in a single pass — per-row blend eliminates flicker */
@@ -266,9 +336,9 @@ static void draw_card_fade(u32 *fb, i64 x, i64 y, i64 w, i64 h, i64 radius, u32
}
static void draw_rounded_input(u32 *fb, i64 x, i64 y, i64 w, i64 h, const char *value, int mask, u32 fg, u32 card_bg, int active) {
u32 fill = active ? 0xFFFFFFFF : INPUT_BG;
fill_rounded_rect(fb, x, y, w, h, 8, fill);
stroke_rounded_rect(fb, x, y, w, h, 8, 1, active ? 0xFFB6C9DD : card_bg);
u32 fill = active ? 0xFF45454Au : INPUT_BG;
fill_rounded_rect(fb, x, y, w, h, 2, fill);
stroke_rounded_rect(fb, x, y, w, h, 2, 1, active ? 0xFF6C6C72 : 0xFF404044);
char out[64]; int i=0;
while(value[i] && i<60) { out[i] = mask ? '*' : value[i]; i++; }
out[i]=0;
@@ -387,17 +457,35 @@ static void cursor_draw(u32 *fb, i64 mx, i64 my) {
static void redraw_setup_card(u32 *fb, i64 card_x, i64 card_y, char *pc, char *user, char *pass, int active, u32 bg, u32 fg, i64 mx, i64 my) {
u32 card = CARD_BG;
u32 sub_fg = 0xFF9098A0; /* dimmer English subtitle color on card */
cursor_erase(fb);
fill_gradient_rect(fb, card_x - 24, card_y - 24, 948, 648);
draw_card(fb, card_x, card_y, 900, 600, card, bg);
fb_bitmap_alpha(fb, g_txt_title, g_txt_title_w, g_txt_title_h, card_x+40, card_y+40, fg, card, 255);
fb_text(fb, "Account Setup", card_x+40, card_y+40+g_txt_title_h+2, sub_fg, card);
fb_bitmap_alpha(fb, g_txt_computer, g_txt_computer_w, g_txt_computer_h, card_x+40, card_y+130, fg, card, 255);
fb_text(fb, "Computer Name", card_x+40, card_y+130+g_txt_computer_h+2, sub_fg, card);
draw_rounded_input(fb, card_x+40, card_y+180, 820, 44, pc, 0, fg, card, active==0);
fb_bitmap_alpha(fb, g_txt_username, g_txt_username_w, g_txt_username_h, card_x+40, card_y+280, fg, card, 255);
fb_text(fb, "Username", card_x+40, card_y+280+g_txt_username_h+2, sub_fg, card);
draw_rounded_input(fb, card_x+40, card_y+330, 820, 44, user, 0, fg, card, active==1);
fb_bitmap_alpha(fb, g_txt_password, g_txt_password_w, g_txt_password_h, card_x+40, card_y+430, fg, card, 255);
fb_text(fb, "Password", card_x+40, card_y+430+g_txt_password_h+2, sub_fg, card);
draw_rounded_input(fb, card_x+40, card_y+480, 820, 44, pass, 1, fg, card, active==2);
fb_bitmap_alpha(fb, g_txt_hint, g_txt_hint_w, g_txt_hint_h, card_x+40, card_y+555, 0xFF7F97AC, card, 255);
fb_text(fb, "Press Enter to confirm each field", card_x+40, card_y+555+g_txt_hint_h+2, 0xFF6F7F8C, card);
cursor_draw(fb, mx, my);
}
/* 仅重绘当前活动输入框,避免每次按键重绘整个卡片导致闪烁 */
static void redraw_input_only(u32 *fb, i64 card_x, i64 card_y, int field, char *pc, char *user, char *pass, i64 mx, i64 my, u32 fg) {
u32 card = CARD_BG;
char *buf = field==0 ? pc : (field==1 ? user : pass);
int mask = (field == 2) ? 1 : 0;
i64 fy = card_y + 180 + (i64)field * 150;
cursor_erase(fb);
fill_rect(fb, card_x + 40, fy, 820, 44, card);
draw_rounded_input(fb, card_x+40, fy, 820, 44, buf, mask, fg, card, 1);
cursor_draw(fb, mx, my);
}
@@ -474,7 +562,7 @@ static int read_field(u32 *fb, i64 card_x, i64 card_y, int field, char *pc, char
buf[len++] = c;
buf[len] = 0;
}
redraw_setup_card(fb, card_x, card_y, pc, user, pass, field, bg, fg, *mx, *my);
redraw_input_only(fb, card_x, card_y, field, pc, user, pass, *mx, *my, fg);
}
}
@@ -486,15 +574,64 @@ typedef struct {
int theme;
} setup_prefs;
static const char *pref_value(int row, int val) {
if (row == 0) return val ? "English" : "Simplified Chinese";
if (row == 1) return val ? "Global" : "China Mainland";
if (row == 2) return val ? "UTC" : "Asia/Shanghai";
if (row == 3) return val ? "CN-QWERTY" : "US-QWERTY";
if (row == 4) return val ? "Dark" : "Light";
/* 语言选项: 索引 0-11 */
static const char *lang_names[] = {
"Simplified Chinese", "English (US)", "English (UK)", "Japanese",
"Korean", "French", "German", "Spanish",
"Portuguese", "Russian", "Arabic", "Hindi"
};
#define LANG_COUNT 12
/* 键盘布局选项: 索引 0-8 */
static const char *kb_names[] = {
"US-QWERTY", "CN-QWERTY", "UK-QWERTY", "JP-JIS",
"KR-104", "FR-AZERTY", "DE-QWERTZ", "ES-QWERTY",
"RU-JCUKEN"
};
#define KB_COUNT 9
/* 区域选项 */
static const char *region_names[] = {
"China Mainland", "Global", "United States", "United Kingdom",
"Japan", "Korea", "France", "Germany",
"Spain", "Russia"
};
#define REGION_COUNT 10
/* 时区选项 */
static const char *tz_names[] = {
"Asia/Shanghai", "UTC", "America/New_York", "America/Los_Angeles",
"Europe/London", "Asia/Tokyo", "Asia/Seoul", "Europe/Paris",
"Europe/Berlin", "Europe/Moscow"
};
#define TZ_COUNT 10
/* 主题选项 */
static const char *theme_names[] = { "Light", "Dark", "Auto" };
#define THEME_COUNT 3
static int pref_option_count(int row) {
if (row == 0) return LANG_COUNT;
if (row == 1) return REGION_COUNT;
if (row == 2) return TZ_COUNT;
if (row == 3) return KB_COUNT;
if (row == 4) return THEME_COUNT;
return 2;
}
static const char *pref_option_name(int row, int opt) {
if (row == 0) return lang_names[opt];
if (row == 1) return region_names[opt];
if (row == 2) return tz_names[opt];
if (row == 3) return kb_names[opt];
if (row == 4) return theme_names[opt];
return "";
}
static const char *pref_value(int row, int val) {
return pref_option_name(row, val);
}
static int pref_get(const setup_prefs *p, int row) {
if (row == 0) return p->language;
if (row == 1) return p->region;
@@ -504,22 +641,41 @@ static int pref_get(const setup_prefs *p, int row) {
return 0;
}
static void pref_set(setup_prefs *p, int row, int val) {
if (row == 0) p->language = val;
else if (row == 1) p->region = val;
else if (row == 2) p->timezone = val;
else if (row == 3) p->keyboard = val;
else if (row == 4) p->theme = val;
}
static void pref_toggle(setup_prefs *p, int row) {
if (row == 0) p->language ^= 1;
else if (row == 1) p->region ^= 1;
else if (row == 2) p->timezone ^= 1;
else if (row == 3) p->keyboard ^= 1;
else if (row == 4) p->theme ^= 1;
int count = pref_option_count(row);
int cur = pref_get(p, row);
pref_set(p, row, (cur + 1) % count);
}
static void draw_option_row(u32 *fb, i64 x, i64 y, i64 w, i64 h,
const u8 *label, i64 lw, i64 lh,
const char *value, int active, u32 fg, u32 card) {
u32 fill = active ? 0xFFFFFFFF : INPUT_BG;
const char *value, int active, u32 fg, u32 card,
const char *en) {
u32 fill = active ? 0xFF45454Au : INPUT_BG;
fb_bitmap_alpha(fb, label, lw, lh, x, y + 4, fg, card, 255);
fill_rounded_rect(fb, x + 220, y, w - 220, h, 8, fill);
stroke_rounded_rect(fb, x + 220, y, w - 220, h, 8, 1, active ? 0xFFB6C9DD : card);
if (en) fb_text(fb, en, x, y + 4 + lh + 1, 0xFF9098A0, card);
fill_rounded_rect(fb, x + 220, y, w - 220, h, 2, fill);
stroke_rounded_rect(fb, x + 220, y, w - 220, h, 2, 1, active ? 0xFF6C6C72 : 0xFF404044);
fb_text(fb, value, x + 238, y + (h - ASCII_H) / 2, fg, fill);
/* dropdown arrow */
{
i64 ax = x + w - 22;
i64 ay = y + (h - 6) / 2;
fill_rect(fb, ax, ay, 11, 1, fg);
fill_rect(fb, ax + 1, ay + 1, 9, 1, fg);
fill_rect(fb, ax + 2, ay + 2, 7, 1, fg);
fill_rect(fb, ax + 3, ay + 3, 5, 1, fg);
fill_rect(fb, ax + 4, ay + 4, 3, 1, fg);
fill_rect(fb, ax + 5, ay + 5, 1, 1, fg);
}
}
static void redraw_prefs_card(u32 *fb, i64 card_x, i64 card_y, setup_prefs *prefs, int active, i64 mx, i64 my, u32 bg, u32 fg) {
@@ -528,12 +684,14 @@ static void redraw_prefs_card(u32 *fb, i64 card_x, i64 card_y, setup_prefs *pref
fill_gradient_rect(fb, card_x - 24, card_y - 24, 948, 648);
draw_card(fb, card_x, card_y, 900, 600, card, bg);
fb_bitmap_alpha(fb, g_txt_prefs_title, g_txt_prefs_title_w, g_txt_prefs_title_h, card_x+40, card_y+40, fg, card, 255);
draw_option_row(fb, card_x+40, card_y+120, 820, 42, g_txt_prefs_language, g_txt_prefs_language_w, g_txt_prefs_language_h, pref_value(0, pref_get(prefs, 0)), active==0, fg, card);
draw_option_row(fb, card_x+40, card_y+200, 820, 42, g_txt_prefs_region, g_txt_prefs_region_w, g_txt_prefs_region_h, pref_value(1, pref_get(prefs, 1)), active==1, fg, card);
draw_option_row(fb, card_x+40, card_y+280, 820, 42, g_txt_prefs_timezone, g_txt_prefs_timezone_w, g_txt_prefs_timezone_h, pref_value(2, pref_get(prefs, 2)), active==2, fg, card);
draw_option_row(fb, card_x+40, card_y+360, 820, 42, g_txt_prefs_keyboard, g_txt_prefs_keyboard_w, g_txt_prefs_keyboard_h, pref_value(3, pref_get(prefs, 3)), active==3, fg, card);
draw_option_row(fb, card_x+40, card_y+440, 820, 42, g_txt_prefs_theme, g_txt_prefs_theme_w, g_txt_prefs_theme_h, pref_value(4, pref_get(prefs, 4)), active==4, fg, card);
fb_text(fb, "Preferences", card_x+40, card_y+40+g_txt_prefs_title_h+2, 0xFF9098A0, card);
draw_option_row(fb, card_x+40, card_y+120, 820, 42, g_txt_prefs_language, g_txt_prefs_language_w, g_txt_prefs_language_h, pref_value(0, pref_get(prefs, 0)), active==0, fg, card, "Language");
draw_option_row(fb, card_x+40, card_y+200, 820, 42, g_txt_prefs_region, g_txt_prefs_region_w, g_txt_prefs_region_h, pref_value(1, pref_get(prefs, 1)), active==1, fg, card, "Region");
draw_option_row(fb, card_x+40, card_y+280, 820, 42, g_txt_prefs_timezone, g_txt_prefs_timezone_w, g_txt_prefs_timezone_h, pref_value(2, pref_get(prefs, 2)), active==2, fg, card, "Timezone");
draw_option_row(fb, card_x+40, card_y+360, 820, 42, g_txt_prefs_keyboard, g_txt_prefs_keyboard_w, g_txt_prefs_keyboard_h, pref_value(3, pref_get(prefs, 3)), active==3, fg, card, "Keyboard");
draw_option_row(fb, card_x+40, card_y+440, 820, 42, g_txt_prefs_theme, g_txt_prefs_theme_w, g_txt_prefs_theme_h, pref_value(4, pref_get(prefs, 4)), active==4, fg, card, "Theme");
fb_bitmap_alpha(fb, g_txt_prefs_hint, g_txt_prefs_hint_w, g_txt_prefs_hint_h, card_x+40, card_y+555, 0xFF7F97AC, card, 255);
fb_text(fb, "Tab/arrows to navigate, Enter to confirm", card_x+40, card_y+555+g_txt_prefs_hint_h+2, 0xFF6F7F8C, card);
cursor_draw(fb, mx, my);
}
@@ -541,21 +699,52 @@ static void redraw_prefs_row(u32 *fb, i64 card_x, i64 card_y, setup_prefs *prefs
u32 card = CARD_BG;
i64 ry = card_y + 120 + (i64)row * 80;
cursor_erase(fb);
fill_rect(fb, card_x + 36, ry - 8, 832, 58, card);
if (row == 0) draw_option_row(fb, card_x+40, ry, 820, 42, g_txt_prefs_language, g_txt_prefs_language_w, g_txt_prefs_language_h, pref_value(0, pref_get(prefs, 0)), active==0, fg, card);
else if (row == 1) draw_option_row(fb, card_x+40, ry, 820, 42, g_txt_prefs_region, g_txt_prefs_region_w, g_txt_prefs_region_h, pref_value(1, pref_get(prefs, 1)), active==1, fg, card);
else if (row == 2) draw_option_row(fb, card_x+40, ry, 820, 42, g_txt_prefs_timezone, g_txt_prefs_timezone_w, g_txt_prefs_timezone_h, pref_value(2, pref_get(prefs, 2)), active==2, fg, card);
else if (row == 3) draw_option_row(fb, card_x+40, ry, 820, 42, g_txt_prefs_keyboard, g_txt_prefs_keyboard_w, g_txt_prefs_keyboard_h, pref_value(3, pref_get(prefs, 3)), active==3, fg, card);
else if (row == 4) draw_option_row(fb, card_x+40, ry, 820, 42, g_txt_prefs_theme, g_txt_prefs_theme_w, g_txt_prefs_theme_h, pref_value(4, pref_get(prefs, 4)), active==4, fg, card);
fill_rounded_rect(fb, card_x + 36, ry - 8, 832, 58, 2, card);
if (row == 0) draw_option_row(fb, card_x+40, ry, 820, 42, g_txt_prefs_language, g_txt_prefs_language_w, g_txt_prefs_language_h, pref_value(0, pref_get(prefs, 0)), active==0, fg, card, "Language");
else if (row == 1) draw_option_row(fb, card_x+40, ry, 820, 42, g_txt_prefs_region, g_txt_prefs_region_w, g_txt_prefs_region_h, pref_value(1, pref_get(prefs, 1)), active==1, fg, card, "Region");
else if (row == 2) draw_option_row(fb, card_x+40, ry, 820, 42, g_txt_prefs_timezone, g_txt_prefs_timezone_w, g_txt_prefs_timezone_h, pref_value(2, pref_get(prefs, 2)), active==2, fg, card, "Timezone");
else if (row == 3) draw_option_row(fb, card_x+40, ry, 820, 42, g_txt_prefs_keyboard, g_txt_prefs_keyboard_w, g_txt_prefs_keyboard_h, pref_value(3, pref_get(prefs, 3)), active==3, fg, card, "Keyboard");
else if (row == 4) draw_option_row(fb, card_x+40, ry, 820, 42, g_txt_prefs_theme, g_txt_prefs_theme_w, g_txt_prefs_theme_h, pref_value(4, pref_get(prefs, 4)), active==4, fg, card, "Theme");
cursor_draw(fb, mx, my);
}
/* 绘制下拉菜单:最多显示 6 项,超出时显示滚动指示 */
static void draw_dropdown_menu(u32 *fb, i64 card_x, i64 card_y, int row, const setup_prefs *prefs, u32 fg) {
i64 dd_x = card_x + 260;
i64 dd_y = card_y + 120 + (i64)row * 80 + 42;
i64 dd_w = 600;
int current = pref_get(prefs, row);
int opt_count = pref_option_count(row);
int visible = opt_count > 6 ? 6 : opt_count;
i64 opt_h = 32;
i64 total_h = visible * opt_h + (opt_count > 6 ? 20 : 0);
/* 深色背景 */
fill_rect(fb, dd_x, dd_y, dd_w, total_h, 0xFF2A2A2E);
/* 选项 */
for (int opt = 0; opt < visible; opt++) {
u32 bg_opt = (opt == current) ? 0xFF45454A : 0xFF2A2A2E;
fill_rect(fb, dd_x, dd_y + opt * opt_h, dd_w, opt_h, bg_opt);
fb_text(fb, pref_option_name(row, opt), dd_x + 18, dd_y + opt * opt_h + (opt_h - ASCII_H) / 2, fg, bg_opt);
}
/* 超过 6 项时显示滚动指示 */
if (opt_count > 6) {
fill_rect(fb, dd_x, dd_y + visible * opt_h, dd_w, 20, 0xFF252528);
fb_text(fb, "...more", dd_x + 18, dd_y + visible * opt_h + 1, 0xFF808088, 0xFF252528);
}
/* 边框 */
fill_rect(fb, dd_x, dd_y, dd_w, 1, 0xFF505055);
fill_rect(fb, dd_x, dd_y + total_h - 1, dd_w, 1, 0xFF505055);
fill_rect(fb, dd_x, dd_y, 1, total_h, 0xFF505055);
fill_rect(fb, dd_x + dd_w - 1, dd_y, 1, total_h, 0xFF505055);
}
static void read_prefs_page(u32 *fb, i64 card_x, i64 card_y, setup_prefs *prefs, i64 *mx, i64 *my, u32 bg, u32 fg) {
int active = 0;
int mcnt = 0;
u8 mpkt[3];
u8 prev_btns = 0;
int e0 = 0;
int dropdown_open = -1;
redraw_prefs_card(fb, card_x, card_y, prefs, active, *mx, *my, bg, fg);
for (;;) {
u8 st = inb(0x64);
@@ -579,14 +768,40 @@ static void read_prefs_page(u32 *fb, i64 card_x, i64 card_y, setup_prefs *prefs,
cursor_draw(fb, *mx, *my);
}
if ((btns & 1) && !(prev_btns & 1)) {
for (int r = 0; r < 5; r++) {
i64 ry = card_y + 120 + (i64)r * 80;
if (*mx >= card_x + 260 && *mx < card_x + 860 && *my >= ry && *my < ry + 42) {
int old = active;
active = r;
pref_toggle(prefs, active);
if (old != active) redraw_prefs_row(fb, card_x, card_y, prefs, old, active, *mx, *my, fg);
redraw_prefs_row(fb, card_x, card_y, prefs, active, active, *mx, *my, fg);
if (dropdown_open >= 0) {
/* 检查点击是否在下拉选项上 */
i64 dd_x = card_x + 260;
i64 dd_y = card_y + 120 + (i64)dropdown_open * 80 + 42;
i64 dd_w = 600;
int opt_count = pref_option_count(dropdown_open);
int visible = opt_count > 6 ? 6 : opt_count;
i64 opt_h = 32;
int clicked_opt = -1;
for (int opt = 0; opt < visible; opt++) {
i64 opt_y = dd_y + opt * opt_h;
if (*mx >= dd_x && *mx < dd_x + dd_w && *my >= opt_y && *my < opt_y + opt_h) {
clicked_opt = opt;
break;
}
}
if (clicked_opt >= 0) {
pref_set(prefs, dropdown_open, clicked_opt);
}
dropdown_open = -1;
redraw_prefs_card(fb, card_x, card_y, prefs, active, *mx, *my, bg, fg);
} else {
/* No dropdown open - check for value area clicks */
for (int r = 0; r < 5; r++) {
i64 ry = card_y + 120 + (i64)r * 80;
if (*mx >= card_x + 260 && *mx < card_x + 860 && *my >= ry && *my < ry + 42) {
active = r;
dropdown_open = r;
redraw_prefs_card(fb, card_x, card_y, prefs, active, *mx, *my, bg, fg);
cursor_erase(fb);
draw_dropdown_menu(fb, card_x, card_y, r, prefs, fg);
cursor_draw(fb, *mx, *my);
break;
}
}
}
}
@@ -596,6 +811,13 @@ static void read_prefs_page(u32 *fb, i64 card_x, i64 card_y, setup_prefs *prefs,
u8 sc = data;
if (sc == 0xE0) { e0 = 1; continue; }
if (sc & 0x80) { e0 = 0; continue; }
/* Close dropdown on any real key press */
if (dropdown_open >= 0) {
dropdown_open = -1;
redraw_prefs_card(fb, card_x, card_y, prefs, active, *mx, *my, bg, fg);
e0 = 0;
continue;
}
if (e0 && sc == 0x48) {
if (active > 0) { int old = active; active--; redraw_prefs_row(fb, card_x, card_y, prefs, old, active, *mx, *my, fg); redraw_prefs_row(fb, card_x, card_y, prefs, active, active, *mx, *my, fg); }
e0 = 0; continue;
@@ -617,6 +839,9 @@ static void read_prefs_page(u32 *fb, i64 card_x, i64 card_y, setup_prefs *prefs,
redraw_prefs_row(fb, card_x, card_y, prefs, old, active, *mx, *my, fg);
redraw_prefs_row(fb, card_x, card_y, prefs, active, active, *mx, *my, fg);
} else {
cursor_bg_valid = 0;
cursor_cur_x = -100;
cursor_cur_y = -100;
return;
}
}
@@ -710,10 +935,11 @@ static void wifi_query_scan(u64 kernel_api) {
net_scan_count_fn scount = (net_scan_count_fn)*(u64 *)(net_api + 48);
net_scan_result_fn sresult = (net_scan_result_fn)*(u64 *)(net_api + 56);
net_is_wireless_fn iswl = (net_is_wireless_fn)*(u64 *)(net_api + 64);
if (!dcount || !iswl || !scount || !sresult) {
logl("[FirstInit] net scan api incomplete");
return;
}
if (!dcount) { logl("[FirstInit] net device_count fn is null"); return; }
if (!sstart) { logl("[FirstInit] net scan_start fn is null"); return; }
if (!scount) { logl("[FirstInit] net scan_count fn is null"); return; }
if (!sresult) { logl("[FirstInit] net scan_result fn is null"); return; }
if (!iswl) { logl("[FirstInit] net is_wireless fn is null"); return; }
u32 ndev = dcount();
/* 串口日志:设备数 */
{
@@ -723,11 +949,21 @@ static void wifi_query_scan(u64 kernel_api) {
b[p++]='0'+(ndev/10); b[p++]='0'+(ndev%10); b[p]=0;
logl(b);
}
/* 找第一个无线设备 */
/* 找第一个无线设备 - 通过 device_info 检查 flags 而非调用 is_wireless */
int wifi_idx = -1;
for (u32 i = 0; i < ndev; i++) {
int rc = iswl(i);
if (rc == 1) { wifi_idx = (int)i; break; }
/* 使用 device_info 获取 flags,避免直接调用 is_wireless 导致崩溃 */
u64 devinfo_fn = *(u64 *)(net_api + 16);
if (devinfo_fn) {
/* dkm_net_device_info 布局: name(8) + mac(6) + pad(2) + flags(4) = 20 字节 */
u8 info[24];
for (int k = 0; k < 24; k++) info[k] = 0;
int rc = ((int (*)(u32, void *))devinfo_fn)(i, info);
if (rc == 0) {
u32 flags = *(u32 *)(info + 16);
if (flags & 8) { wifi_idx = (int)i; break; } /* DKM_NET_F_WIRELESS = (1<<3) */
}
}
}
if (wifi_idx < 0) {
logl("[FirstInit] no wireless device found");
@@ -806,63 +1042,84 @@ static void net_select(setup_net *net, int idx) {
static void draw_wifi_row(u32 *fb, i64 card_x, i64 card_y, setup_net *net, int row, int active, u32 fg) {
u32 card = CARD_BG;
i64 x = card_x + 40;
i64 y = card_y + 132 + (i64)row * 64;
u32 fill = active == row ? 0xFFFFFFFF : (net->selected == row ? 0xFFDCECF8 : INPUT_BG);
fill_rounded_rect(fb, x, y, 390, 50, 9, fill);
stroke_rounded_rect(fb, x, y, 390, 50, 9, 1, active == row ? 0xFF7FA7C8 : card);
fb_text(fb, net->selected == row ? ">" : " ", x + 12, y + 16, fg, fill);
i64 y = card_y + 132 + (i64)row * 56;
u32 fill = active == row ? 0xFF45454A : (net->selected == row ? 0xFF3A3A3E : INPUT_BG);
fill_rounded_rect(fb, x, y, 390, 46, 3, fill);
stroke_rounded_rect(fb, x, y, 390, 46, 3, 1, active == row ? 0xFF6C6C72 : 0xFF404044);
fb_text(fb, net->selected == row ? ">" : " ", x + 12, y + 14, fg, fill);
if (row < g_wifi_scan_count) {
fb_text(fb, g_wifi_ssids[row], x + 42, y + 8, fg, fill);
fb_text(fb, g_wifi_meta[row], x + 42, y + 28, 0xFF7F97AC, fill);
fb_text(fb, g_wifi_ssids[row], x + 42, y + 6, fg, fill);
fb_text(fb, g_wifi_meta[row], x + 42, y + 26, 0xFF808088, fill);
} else {
/* 无扫描结果时的占位行 */
const char *placeholder = (g_wifi_scan_tried && g_wifi_scan_count == 0)
? "No networks found" : "(empty)";
fb_text(fb, placeholder, x + 42, y + 16, 0xFF9AACC0, fill);
fb_text(fb, placeholder, x + 42, y + 14, 0xFF606068, fill);
}
}
/* 网络页面活跃元素索引 */
#define NET_ACT_WIFI_BASE 0
#define NET_ACT_SSID 4
#define NET_ACT_PASS 5
#define NET_ACT_SAVE 6
#define NET_ACT_CONNECT 7
#define NET_ACT_COUNT 8
static void redraw_network_card(u32 *fb, i64 card_x, i64 card_y, setup_net *net, int active, i64 mx, i64 my, u32 bg, u32 fg) {
u32 card = CARD_BG;
cursor_erase(fb);
fill_gradient_rect(fb, card_x - 24, card_y - 24, 948, 648);
draw_card(fb, card_x, card_y, 900, 600, card, bg);
fb_bitmap_alpha(fb, g_txt_network_title, g_txt_network_title_w, g_txt_network_title_h, card_x+40, card_y+40, fg, card, 255);
fb_text(fb, "Choose a network", card_x + 40, card_y + 88, 0xFF7F97AC, card);
fb_text(fb, "Network", card_x+40, card_y+40+g_txt_network_title_h+2, 0xFF9098A0, card);
fb_text(fb, "Choose or enter network", card_x + 40, card_y + 92, 0xFF808088, card);
/* 左侧: WiFi 列表 */
for (int r=0; r<WIFI_SCAN_COUNT; r++) draw_wifi_row(fb, card_x, card_y, net, r, active, fg);
fill_rounded_rect(fb, card_x + 470, card_y + 120, 390, 260, 12, INPUT_BG);
stroke_rounded_rect(fb, card_x + 470, card_y + 120, 390, 260, 12, 1, 0xFFD2E3F0);
fb_text(fb, "Highlighted network", card_x + 494, card_y + 148, 0xFF7F97AC, INPUT_BG);
int preview = active < WIFI_SCAN_COUNT ? active : net->selected;
if (preview < g_wifi_scan_count) {
fb_text(fb, g_wifi_ssids[preview], card_x + 494, card_y + 182, fg, INPUT_BG);
fb_text(fb, g_wifi_meta[preview], card_x + 494, card_y + 210, 0xFF7F97AC, INPUT_BG);
} else {
fb_text(fb, g_wifi_scan_tried ? "No networks found" : "WiFi not initialized",
card_x + 494, card_y + 182, 0xFF9AACC0, INPUT_BG);
fb_text(fb, g_wifi_scan_tried ? "Check ath9k driver" : "Run ath9k driver first",
card_x + 494, card_y + 210, 0xFF9AACC0, INPUT_BG);
}
fb_text(fb, "Use Up/Down or click menu", card_x + 494, card_y + 260, 0xFF7F97AC, INPUT_BG);
fb_text(fb, "Enter selects highlighted item", card_x + 494, card_y + 288, 0xFF7F97AC, INPUT_BG);
/* 右侧: 手动输入区 */
fb_text(fb, "Network name (SSID)", card_x + 470, card_y + 120, 0xFF808088, card);
draw_rounded_input(fb, card_x + 470, card_y + 148, 390, 44, net->ssid, 0, fg, card, active == NET_ACT_SSID);
fb_text(fb, "Password", card_x + 470, card_y + 210, 0xFF808088, card);
draw_rounded_input(fb, card_x + 470, card_y + 238, 390, 44, net->password, 1, fg, card, active == NET_ACT_PASS);
/* 保存按钮 */
{
u32 btn_bg = active == NET_ACT_SAVE ? 0xFF45454A : 0xFF3A3A3E;
fill_rounded_rect(fb, card_x + 470, card_y + 310, 180, 44, 3, btn_bg);
stroke_rounded_rect(fb, card_x + 470, card_y + 310, 180, 44, 3, 1, active == NET_ACT_SAVE ? 0xFF6C6C72 : 0xFF404044);
fb_text(fb, "Save", card_x + 530, card_y + 322, fg, btn_bg);
}
/* 连接按钮 */
{
u32 btn_bg = active == NET_ACT_CONNECT ? 0xFF4A6A8A : 0xFF3A5070;
fill_rounded_rect(fb, card_x + 680, card_y + 310, 180, 44, 3, btn_bg);
stroke_rounded_rect(fb, card_x + 680, card_y + 310, 180, 44, 3, 1, active == NET_ACT_CONNECT ? 0xFF6A8AAA : 0xFF404060);
fb_text(fb, "Connect", card_x + 725, card_y + 322, 0xFFE0E0E0, btn_bg);
}
/* 状态提示 */
if (net->connected) {
fb_text(fb, "Network profile saved", card_x + 470, card_y + 380, 0xFF5FAF6F, card);
} else if (net->ssid[0]) {
fb_text(fb, "Press Save or Connect", card_x + 470, card_y + 380, 0xFF808088, card);
} else {
fb_text(fb, "Select network or type SSID", card_x + 470, card_y + 380, 0xFF606068, card);
}
fb_text(fb, "Save=save config only", card_x + 470, card_y + 510, 0xFF606068, card);
fb_text(fb, "Connect=save and connect", card_x + 470, card_y + 535, 0xFF606068, card);
fb_text(fb, "Password", card_x + 470, card_y + 400, fg, card);
draw_rounded_input(fb, card_x + 470, card_y + 430, 390, 44, net->password, 1, fg, card, active == WIFI_SCAN_COUNT);
u32 btn = active == WIFI_SCAN_COUNT + 1 ? 0xFFFFFFFF : 0xFFDCECF8;
fill_rounded_rect(fb, card_x + 620, card_y + 500, 240, 48, 10, btn);
stroke_rounded_rect(fb, card_x + 620, card_y + 500, 240, 48, 10, 1, active == WIFI_SCAN_COUNT + 1 ? 0xFFB6C9DD : card);
fb_text(fb, "Connect", card_x + 690, card_y + 516, fg, btn);
if (net->connected) fb_text(fb, "Connection profile saved", card_x + 40, card_y + 515, 0xFF4F8A5F, card);
else fb_text(fb, "Select SSID, enter password, then Connect", card_x + 40, card_y + 555, 0xFF7F97AC, card);
cursor_draw(fb, mx, my);
}
static void read_network_page(u32 *fb, i64 card_x, i64 card_y, setup_net *net, i64 *mx, i64 *my, u32 bg, u32 fg) {
int active = 0, mcnt = 0, e0 = 0, shift = 0;
u8 mpkt[3]; u8 prev_btns = 0;
int len = 0;
while (net->password[len]) len++;
int plen = 0;
int slen = 0;
while (net->password[plen]) plen++;
while (net->ssid[slen]) slen++;
net_select(net, net->selected);
redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg);
for (;;) {
@@ -885,23 +1142,41 @@ static void read_network_page(u32 *fb, i64 card_x, i64 card_y, setup_net *net, i
cursor_draw(fb, *mx, *my);
}
if ((btns & 1) && !(prev_btns & 1)) {
/* WiFi 列表点击 */
for (int r=0; r<WIFI_SCAN_COUNT; r++) {
i64 ry = card_y + 132 + (i64)r * 64;
if (*mx >= card_x + 40 && *mx < card_x + 430 && *my >= ry && *my < ry + 50) {
i64 ry = card_y + 132 + (i64)r * 56;
if (*mx >= card_x + 40 && *mx < card_x + 430 && *my >= ry && *my < ry + 46) {
active = r;
net_select(net, r);
len = 0;
slen = 0;
while (net->ssid[slen]) slen++;
plen = 0;
redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg);
}
}
if (*mx >= card_x + 470 && *mx < card_x + 860 && *my >= card_y + 430 && *my < card_y + 474) {
active = WIFI_SCAN_COUNT;
/* SSID 输入框 */
if (*mx >= card_x + 470 && *mx < card_x + 860 && *my >= card_y + 148 && *my < card_y + 192) {
active = NET_ACT_SSID;
redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg);
}
if (*mx >= card_x + 620 && *mx < card_x + 860 && *my >= card_y + 500 && *my < card_y + 548) {
/* 密码输入框 */
if (*mx >= card_x + 470 && *mx < card_x + 860 && *my >= card_y + 238 && *my < card_y + 282) {
active = NET_ACT_PASS;
redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg);
}
/* 保存按钮 */
if (*mx >= card_x + 470 && *mx < card_x + 650 && *my >= card_y + 310 && *my < card_y + 354) {
net->connected = 1;
logl("[FirstInit] wireless profile selected");
redraw_network_card(fb, card_x, card_y, net, WIFI_SCAN_COUNT + 1, *mx, *my, bg, fg);
logl("[FirstInit] network profile saved");
redraw_network_card(fb, card_x, card_y, net, NET_ACT_SAVE, *mx, *my, bg, fg);
for (int i=0;i<30;i++) delay_frame();
return;
}
/* 连接按钮 */
if (*mx >= card_x + 680 && *mx < card_x + 860 && *my >= card_y + 310 && *my < card_y + 354) {
net->connected = 1;
logl("[FirstInit] network connect requested");
redraw_network_card(fb, card_x, card_y, net, NET_ACT_CONNECT, *mx, *my, bg, fg);
for (int i=0;i<30;i++) delay_frame();
return;
}
@@ -920,7 +1195,7 @@ static void read_network_page(u32 *fb, i64 card_x, i64 card_y, setup_net *net, i
e0=0; continue;
}
if (e0 && sc == 0x50) {
if (active < WIFI_SCAN_COUNT + 1) active++;
if (active < NET_ACT_COUNT - 1) active++;
redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg);
e0=0; continue;
}
@@ -930,13 +1205,22 @@ static void read_network_page(u32 *fb, i64 card_x, i64 card_y, setup_net *net, i
if (c == '\n') {
if (active < WIFI_SCAN_COUNT) {
net_select(net, active);
len = 0;
active = WIFI_SCAN_COUNT;
} else if (active == WIFI_SCAN_COUNT) {
active = WIFI_SCAN_COUNT + 1;
} else {
slen = 0;
while (net->ssid[slen]) slen++;
active = NET_ACT_PASS;
} else if (active == NET_ACT_SSID) {
active = NET_ACT_PASS;
} else if (active == NET_ACT_PASS) {
active = NET_ACT_CONNECT;
} else if (active == NET_ACT_SAVE) {
net->connected = 1;
logl("[FirstInit] wireless profile selected");
logl("[FirstInit] network profile saved");
redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg);
for (int i=0;i<30;i++) delay_frame();
return;
} else if (active == NET_ACT_CONNECT) {
net->connected = 1;
logl("[FirstInit] network connect requested");
redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg);
for (int i=0;i<30;i++) delay_frame();
return;
@@ -945,16 +1229,28 @@ static void read_network_page(u32 *fb, i64 card_x, i64 card_y, setup_net *net, i
continue;
}
if (c == '\t') {
active = (active + 1) % (WIFI_SCAN_COUNT + 2);
active = (active + 1) % NET_ACT_COUNT;
redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg);
continue;
}
if (active == WIFI_SCAN_COUNT) {
/* SSID 输入 */
if (active == NET_ACT_SSID) {
if (c == 8) {
if (len > 0) net->password[--len] = 0;
} else if (len < 63 && c >= 32 && c <= 126) {
net->password[len++] = c;
net->password[len] = 0;
if (slen > 0) net->ssid[--slen] = 0;
} else if (slen < 31 && c >= 32 && c <= 126) {
net->ssid[slen++] = c;
net->ssid[slen] = 0;
}
net->connected = 0;
redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg);
}
/* 密码输入 */
if (active == NET_ACT_PASS) {
if (c == 8) {
if (plen > 0) net->password[--plen] = 0;
} else if (plen < 63 && c >= 32 && c <= 126) {
net->password[plen++] = c;
net->password[plen] = 0;
}
net->connected = 0;
redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg);
@@ -981,9 +1277,12 @@ static void sha256(const char *msg, u8 out[32]) {
for(int i=0;i<8;i++){out[i*4]=(u8)(h[i]>>24);out[i*4+1]=(u8)(h[i]>>16);out[i*4+2]=(u8)(h[i]>>8);out[i*4+3]=(u8)h[i];}
}
static u8 g_conf_data[1024];
static u32 g_conf_size;
static void build_user_conf(const char *pc, const char *user, const char *pass, const setup_prefs *prefs, const setup_net *net) {
u8 hash[32]; sha256(pass, hash);
static char conf[1024]; int p=0;
char *conf = (char *)g_conf_data; int p=0;
const char *pre="DESHAB_USERCONF_V1\ncomputer=";
for(int i=0;pre[i];i++) conf[p++]=pre[i];
for(int i=0;pc[i]&&p<740;i++) conf[p++]=pc[i];
@@ -1011,15 +1310,18 @@ static void build_user_conf(const char *pc, const char *user, const char *pass,
const char *ni="\nnetwork.ip=dhcp"; for(int i=0;ni[i];i++) conf[p++]=ni[i];
const char *dns="\nnetwork.dns=auto"; for(int i=0;dns[i];i++) conf[p++]=dns[i];
conf[p++]='\n';
/* XOR encrypt buffer with hash-derived stream; real disk write waits for FAT32 write support. */
/* XOR encrypt buffer with hash-derived stream; DSK writes to FAT32 after return. */
for(int i=0;i<p;i++) conf[i]^=hash[i&31];
logl("[FirstInit] user.conf encrypted in memory (disk write pending)");
g_conf_size = (u32)p;
logl("[FirstInit] user.conf built and encrypted");
}
__attribute__((visibility("default")))
void dsk_entry(const dsk_boot_context *ctx) {
__asm__ volatile("cli"); /* prevent IRQ1 (ps2kbd) from racing with our polling */
logl("[FirstInit] boot");
logl("[FirstInit] calibrating TSC (实机 CPU 频率计算)");
tsc_calibrate_fi();
if (!ctx || ctx->magic != 0x44534B31424F4F54ULL) {
logl("[FirstInit] bad context");
@@ -1049,13 +1351,20 @@ void dsk_entry(const dsk_boot_context *ctx) {
fade_ring(fb, cx, cy, bg);
logl("[FirstInit] ring faded");
/* center text: Chinese bitmaps */
/* center text: Chinese bitmaps + English subtitle below */
i64 tw = g_txt_welcome_w, tx = cx - tw/2, ty = cy - g_txt_welcome_h/2;
/* English subtitle sits below the Chinese bitmap, centered independently */
const char *welcome_en = "Welcome to Deshab";
i64 en_w = fb_text_width(welcome_en);
i64 en_x = cx - en_w/2, en_y = ty + g_txt_welcome_h + 6;
/* subtitle color: dimmer than WELCOME_FG so it reads as secondary */
u32 sub_fg = 0xFFA8B8C8;
logl("[FirstInit] fading in welcome");
for (u32 a=0; a<=255; a+=17) {
fill_gradient_rect(fb, tx-4, ty-4, tw+8, g_txt_welcome_h+8);
fill_gradient_rect(fb, tx-4, ty-4, tw+8, g_txt_welcome_h + 6 + ASCII_H + 4);
fb_bitmap_alpha(fb, g_txt_welcome, tw, g_txt_welcome_h, tx, ty, WELCOME_FG, bg_at_y(ty + g_txt_welcome_h / 2), a);
fb_text_alpha(fb, welcome_en, en_x, en_y, sub_fg, bg_at_y(en_y + ASCII_H/2), a);
delay_frame();
}
logl("[FirstInit] welcome drawn");
@@ -1064,16 +1373,26 @@ void dsk_entry(const dsk_boot_context *ctx) {
logl("[FirstInit] cross-fading to setup message");
i64 tw2 = g_txt_setup_w, tx2 = cx - tw2/2;
i64 area_x = tx2 < tx ? tx2 : tx;
i64 area_w = (tx2 + tw2 > tx + tw ? tx2 + tw2 : tx + tw) - area_x;
i64 area_h = (g_txt_setup_h > g_txt_welcome_h ? g_txt_setup_h : g_txt_welcome_h) + 8;
const char *setup_en = "Let's set up your system";
i64 en2_w = fb_text_width(setup_en);
i64 en2_x = cx - en2_w/2;
i64 area_x = (tx2 < tx ? tx2 : tx);
if (en_x < area_x) area_x = en_x;
if (en2_x < area_x) area_x = en2_x;
i64 area_right = tx + tw; if (tx2 + tw2 > area_right) area_right = tx2 + tw2;
if (en_x + en_w > area_right) area_right = en_x + en_w;
if (en2_x + en2_w > area_right) area_right = en2_x + en2_w;
i64 area_w = area_right - area_x;
i64 area_h = (g_txt_setup_h > g_txt_welcome_h ? g_txt_setup_h : g_txt_welcome_h) + 6 + ASCII_H + 8;
for (u32 step=0; step<=15; step++) {
u32 a2 = step * 17;
u32 a1 = 255 - a2;
fill_gradient_rect(fb, area_x-4, ty-4, area_w+8, area_h);
fb_bitmap_alpha(fb, g_txt_welcome, tw, g_txt_welcome_h, tx, ty, WELCOME_FG, bg_at_y(ty + g_txt_welcome_h / 2), a1);
fb_text_alpha(fb, welcome_en, en_x, en_y, sub_fg, bg_at_y(en_y + ASCII_H/2), a1);
fb_bitmap_alpha(fb, g_txt_setup, tw2, g_txt_setup_h, tx2, ty, WELCOME_FG, bg_at_y(ty + g_txt_setup_h / 2), a2);
fb_text_alpha(fb, setup_en, en2_x, en_y, sub_fg, bg_at_y(en_y + ASCII_H/2), a2);
delay_frame();
}
logl("[FirstInit] setup message drawn");
@@ -1089,13 +1408,14 @@ void dsk_entry(const dsk_boot_context *ctx) {
logl("[FI] fadein start");
/* Phase 1: fade in card background only (no title) — avoids title/card color mismatch flicker */
for (u32 a=0; a<=255; a+=17) {
draw_card_fade(fb, card_x, card_y, 900, 600, 12, a);
draw_card_fade(fb, card_x, card_y, 900, 600, 4, a);
delay_frame();
}
/* Phase 2: draw title once on fully-rendered card */
{
u32 title_bg = blend(bg_at_y(card_y + 40 + g_txt_title_h/2), CARD_BG, 255);
fb_bitmap_alpha(fb, g_txt_title, g_txt_title_w, g_txt_title_h, card_x+40, card_y+40, fg, title_bg, 255);
fb_text(fb, "Account Setup", card_x+40, card_y+40+g_txt_title_h+2, 0xFF9098A0, title_bg);
}
logl("[FI] fadein done");
logl("[FirstInit] initializing PS/2 mouse");
@@ -1103,10 +1423,89 @@ void dsk_entry(const dsk_boot_context *ctx) {
i64 mx = cx, my = cy;
{
int field = 0;
while (field < 3) {
field = read_field(fb, card_x, card_y, field, pc, user, pass, &mx, &my, bg, fg);
for (;;) {
while (field < 3) {
field = read_field(fb, card_x, card_y, field, pc, user, pass, &mx, &my, bg, fg);
}
/* Validate: all fields must be non-empty */
if (pc[0] != 0 && user[0] != 0 && pass[0] != 0) break;
/* Show warning message on card (overwrite hint area) */
cursor_erase(fb);
{
u32 card = CARD_BG;
fill_rect(fb, card_x + 40, card_y + 550, 820, 45, card);
fb_text(fb, "Please fill in all fields", card_x + 40, card_y + 555, 0xFFFF6B6B, card);
fb_text(fb, "Press Enter to continue", card_x + 40, card_y + 575, 0xFFC0C0C0, card);
}
cursor_draw(fb, mx, my); /* 重绘鼠标光标,避免校验失败时光标消失 */
logl("[FI] validation failed: empty field(s)");
/* 等待用户确认:Enter/任意键回到 field 0,或点击输入框切换字段。
* 避免死等 Enter 导致用户点击/输入无响应。 */
int next_field = 0;
{
int release = 0;
int mcnt = 0;
u8 mpkt[3];
u8 prev_btns = 0;
int done = 0;
while (!done) {
u8 st = inb(0x64);
if (!(st & 1)) { __asm__("pause"); continue; }
u8 data = inb(0x60);
if (st & 0x20) {
/* 鼠标数据:组装 3 字节包,同步位 bit 3 */
if (mcnt == 0 && !(data & 0x08)) continue;
mpkt[mcnt++] = data;
if (mcnt < 3) continue;
mcnt = 0;
i64 dx = (i64)(i8)mpkt[1];
i64 dy = (i64)(i8)mpkt[2];
u8 btns = mpkt[0] & 0x07;
if (dx != 0 || dy != 0) {
cursor_erase(fb);
mx += dx;
my -= dy; /* invert Y: 鼠标上移 = 屏幕上移 */
if (mx < 0) mx = 0;
if (my < 0) my = 0;
if ((u64)mx >= fb_w) mx = (i64)fb_w - 1;
if ((u64)my >= fb_h) my = (i64)fb_h - 1;
cursor_draw(fb, mx, my);
}
/* 左键点击输入框:切换到该字段重新输入 */
if ((btns & 1) && !(prev_btns & 1)) {
for (int f = 0; f < 3; f++) {
i64 fy = card_y + 180 + (i64)f * 150;
if (my >= fy && my < fy + 44 && mx >= card_x + 40 && mx < card_x + 860) {
next_field = f;
done = 1;
break;
}
}
}
prev_btns = btns;
continue;
}
u8 sc = data;
if (sc == 0xF0) { release = 1; continue; }
if (release) { release = 0; continue; }
if (sc & 0x80) continue; /* Set 1 release code */
/* Enter 或任意按键:退出等待,回到 field 0 重新输入 */
next_field = 0;
done = 1;
}
}
/* Reset all fields and restart input from next_field.
* read_field() will call redraw_setup_card() which fully
* redraws the card (title, labels, inputs, hint) and cursor. */
pc[0] = 0; user[0] = 0; pass[0] = 0;
field = next_field;
}
}
logl("[FI] all fields done");
/* 重置光标状态,避免页面切换时崩溃 */
cursor_bg_valid = 0;
cursor_cur_x = -100;
cursor_cur_y = -100;
setup_prefs prefs;
prefs.language = 0; /* zh-CN */
prefs.region = 0; /* CN */
@@ -1114,6 +1513,10 @@ void dsk_entry(const dsk_boot_context *ctx) {
prefs.keyboard = 0; /* US-QWERTY */
prefs.theme = 0; /* Light */
read_prefs_page(fb, card_x, card_y, &prefs, &mx, &my, bg, fg);
/* 重置光标状态,避免页面切换时崩溃 */
cursor_bg_valid = 0;
cursor_cur_x = -100;
cursor_cur_y = -100;
/* 查询真实 WiFi 扫描结果(来自 ath9k 驱动通过 kernel_api.net.scan_*) */
wifi_query_scan(ctx->dkm_kernel_api);
setup_net net;
@@ -1121,23 +1524,36 @@ void dsk_entry(const dsk_boot_context *ctx) {
net.connected = 0;
net.ssid[0] = 0;
net.password[0] = 0;
if (g_no_wireless_device) {
/* QEMU 或无无线网卡场景:跳过 WiFi 设置页面,network.mode=disabled */
logl("[FirstInit] no wireless device, skipping WiFi setup page");
net.connected = 0;
} else {
cursor_bg_valid = 0;
cursor_cur_x = -100;
cursor_cur_y = -100;
logl("[FirstInit] entering wireless setup page");
read_network_page(fb, card_x, card_y, &net, &mx, &my, bg, fg);
logl("[FirstInit] wireless setup page returned");
}
/* 始终显示网络设置页面,即使无无线设备也展示页面让用户确认 */
cursor_bg_valid = 0;
cursor_cur_x = -100;
cursor_cur_y = -100;
logl("[FirstInit] entering network setup page");
read_network_page(fb, card_x, card_y, &net, &mx, &my, bg, fg);
logl("[FirstInit] network setup page returned");
build_user_conf(pc, user, pass, &prefs, &net);
/* Pass config buffer to DSK via boot context reserved fields */
{
dsk_boot_context *ctx_mut = (dsk_boot_context *)ctx;
ctx_mut->reserved[0] = (u64)(u64*)g_conf_data;
ctx_mut->reserved[1] = (u64)g_conf_size;
}
logl("[FirstInit] config ready for DSK to write");
cursor_erase(fb);
fill_gradient_rect(fb, card_x - 24, card_y - 24, 948, 648);
fb_bitmap_alpha(fb, g_txt_done, g_txt_done_w, g_txt_done_h, cx - g_txt_done_w/2, cy - 20, fg, bg_at_y(cy), 255);
fb_bitmap_alpha(fb, g_txt_done, g_txt_done_w, g_txt_done_h, cx - g_txt_done_w/2, cy - 30, fg, bg_at_y(cy), 255);
/* English subtitle below the Chinese done bitmap */
{ const char *done_en = "Setup Complete";
i64 dw = fb_text_width(done_en);
fb_text(fb, done_en, cx - dw/2, cy + 2, 0xFFA8B8C8, bg_at_y(cy + 10));
}
/* status line below the subtitle */
{ const char *status = "System starting...";
i64 sw = fb_text_width(status);
fb_text(fb, status, cx - sw/2, cy + 28, 0xFF808088, bg_at_y(cy + 36));
}
logl("[FirstInit] user setup finished");
/* 短暂停留后返回,由 DSK 接管加载 shell */