/* Deshab Settings - 系统设置工具 * * 内核页 (KERNEL): 查看/编辑 FUCK 内核配置(boot/drivers/dsk/debug/utsm 常用键), * 行保留方式序列化(注释与未暴露键不受影响),写回 * /system/deshab64/FUCK(缺失时回退根目录 FUCK)。 * 网络页 (NETWORK): 查看/编辑网络配置,全量重写根目录 NETCONF.CNF(netman 读取)。 * 设备页 (DEVICES): 实时枚举内核 netdev 注册表,选择连接设备, * WiFi 扫描结果查看与 SSID 选择(选择写入 NETCONF.CNF)。 * * 快捷键: F1 内核页 F2 网络页 F3 设备页 Tab 切页 F5 保存 F6 重载 Esc 退出 * Up/Down 选择字段 Space/Enter/Left/Right 切换布尔/枚举/整数 * 文本字段选中后直接键入编辑,Backspace 删除 * 设备页: S 扫描WiFi Enter 选择设备/SSID * 鼠标: 点击 tab / 字段行 / 按钮 * * 注意: 配置修改在下次启动后生效(FUCK 由 Limine boot module 加载, * NETCONF.CNF 由 netman.elf 在启动时读取)。 */ #include "../../firstInit/ascii_bitmaps.c" #include "../desktop_app.h" #include "../fat32_io.h" /* ---- Net API 类型(与 dkm_net_api 布局一致,偏移访问) ---- */ typedef u32 (*net_device_count_fn)(void); typedef int (*net_device_info_fn)(u32 index, void *out); typedef int (*net_scan_start_fn)(u32 index); typedef int (*net_scan_count_fn)(u32 index); typedef int (*net_scan_result_fn)(u32 index, u32 n, void *out); typedef int (*net_is_wireless_fn)(u32 index); #define NM_NET_F_LINK_UP (1u << 0) #define NM_NET_F_TX_READY (1u << 1) #define NM_NET_F_RX_READY (1u << 2) #define NM_NET_F_WIRELESS (1u << 3) #define NM_SEC_OPEN 0u #define NM_SEC_WEP 1u #define NM_SEC_WPA 2u #define NM_SEC_WPA2 3u #define NM_SEC_WPA3 4u typedef struct { const char *name; u8 mac[6]; u32 flags; } nm_device_info; typedef struct { char ssid[33]; u8 bssid[6]; u8 channel; signed char rssi; u8 security; } nm_scan_result; /* ---- 字段模型 ---- */ typedef enum { FT_BOOL = 0, FT_INT, FT_TEXT, FT_CHOICE } sftype; typedef struct { const char *section; /* INI section */ const char *key; /* 键名(同时作显示名) */ sftype type; const char *choices; /* FT_CHOICE: "|" 分隔,如 "dhcp|static" */ int min, max; /* FT_INT 范围 */ char value[48]; int dirty; } sfield; /* ---- 内核页字段(FUCK) ---- */ static sfield g_kfields[] = { /* boot */ { "boot", "dev_mode", FT_BOOL, 0, 0, 0, "1", 0 }, { "boot", "selftest", FT_BOOL, 0, 0, 0, "1", 0 }, { "boot", "vmm", FT_BOOL, 0, 0, 0, "1", 0 }, { "boot", "linux_guest", FT_BOOL, 0, 0, 0, "1", 0 }, /* drivers */ { "drivers", "e1000", FT_BOOL, 0, 0, 0, "1", 0 }, { "drivers", "virtio_net", FT_BOOL, 0, 0, 0, "1", 0 }, { "drivers", "nvme", FT_BOOL, 0, 0, 0, "1", 0 }, { "drivers", "ahci", FT_BOOL, 0, 0, 0, "1", 0 }, { "drivers", "apic_route", FT_BOOL, 0, 0, 0, "1", 0 }, { "drivers", "apic_route_legacy", FT_BOOL, 0, 0, 0, "1", 0 }, { "drivers", "nvme_msi", FT_BOOL, 0, 0, 0, "1", 0 }, { "drivers", "ath9k", FT_BOOL, 0, 0, 0, "0", 0 }, /* dsk */ { "dsk", "show_logo", FT_BOOL, 0, 0, 0, "1", 0 }, { "dsk", "skip_login", FT_BOOL, 0, 0, 0, "1", 0 }, { "dsk", "mouse_init", FT_BOOL, 0, 0, 0, "1", 0 }, { "dsk", "netman", FT_BOOL, 0, 0, 0, "1", 0 }, { "dsk", "default_shell", FT_TEXT, 0, 0, 0, "cmd", 0 }, /* debug */ { "debug", "enabled", FT_BOOL, 0, 0, 0, "1", 0 }, { "debug", "log_level", FT_INT, 0, 0, 5, "1", 0 }, { "debug", "net_trace", FT_BOOL, 0, 0, 0, "0", 0 }, /* utsm 算法参数 */ { "utsm", "max_segments", FT_TEXT, 0, 0, 0, "1024", 0 }, { "utsm", "max_capabilities", FT_TEXT, 0, 0, 0, "256", 0 }, { "utsm", "max_pckc_keys", FT_TEXT, 0, 0, 0, "8", 0 }, { "utsm", "dirty_shard_pages", FT_TEXT, 0, 0, 0, "1024", 0 }, { "utsm", "arena_size_mb", FT_TEXT, 0, 0, 0, "64", 0 }, { "utsm", "drr_emergency_pool_kb", FT_TEXT, 0, 0, 0, "64", 0 }, { "utsm", "drr_recovery_log_size", FT_TEXT, 0, 0, 0, "4096", 0 }, { "utsm", "drr_heartbeat_interval", FT_TEXT, 0, 0, 0, "0", 0 }, }; #define KFIELD_COUNT (sizeof(g_kfields)/sizeof(g_kfields[0])) /* ---- 网络页字段(NETCONF.CNF) ---- */ static sfield g_nfields[] = { { "network", "mode", FT_CHOICE, "dhcp|static|disabled", 0, 0, "dhcp", 0 }, { "network", "device", FT_TEXT, 0, 0, 0, "auto", 0 }, { "network", "ip", FT_TEXT, 0, 0, 0, "dhcp", 0 }, { "network", "gateway", FT_TEXT, 0, 0, 0, "auto", 0 }, { "network", "dns", FT_TEXT, 0, 0, 0, "auto", 0 }, { "network", "name", FT_TEXT, 0, 0, 0, "", 0 }, { "network", "password", FT_TEXT, 0, 0, 0, "", 0 }, }; #define NFIELD_COUNT (sizeof(g_nfields)/sizeof(g_nfields[0])) #define NF_DEVICE 1 /* g_nfields 中 device 字段索引 */ #define NF_NAME 5 /* g_nfields 中 name 字段索引 */ /* ---- 全局状态 ---- */ static da_app_context g_ac; static da_cursor g_cursor; static da_mouse g_mouse; static int g_page = 0; /* 0=kernel 1=network 2=devices */ static int g_sel = 0; /* 当前页选中字段 */ static int g_scroll = 0; /* 当前页滚动偏移 */ static int g_sel_saved[3] = { 0, 0, 0 }; static int g_scroll_saved[3] = { 0, 0, 0 }; static char g_status[96]; /* 状态栏消息 */ static u32 g_status_color = DA_SUCCESS; static int g_fuck_loaded = 0; /* FUCK 原文是否读到 */ static int g_net_loaded = 0; /* 配置文本缓冲 */ static char g_fuck_text[12288]; /* FUCK 原文 */ static u32 g_fuck_len = 0; static char g_fuck_out[16384]; /* FUCK 序列化输出 */ static char g_conf_text[4096]; /* NETCONF 原文 */ static char g_conf_out[2048]; /* NETCONF 序列化输出 */ /* ---- Net API 函数指针 ---- */ static net_device_count_fn g_net_device_count; static net_device_info_fn g_net_device_info; static net_scan_start_fn g_net_scan_start; static net_scan_count_fn g_net_scan_count; static net_scan_result_fn g_net_scan_result; static net_is_wireless_fn g_net_is_wireless; /* ---- 设备页状态 ---- */ #define DEV_MAX 8 #define SCAN_MAX 32 static nm_device_info g_devs[DEV_MAX]; static int g_dev_count = 0; static nm_scan_result g_scans[SCAN_MAX]; static int g_scan_count = 0; static int g_scan_dev_idx = -1; /* 上次扫描的设备索引 */ static int g_net_avail = 0; /* net API 是否可用 */ /* 设备页 item 布局: * 0..g_dev_count-1 : 设备行 * g_dev_count : 分隔线 (不可选) * g_dev_count+1..+g_scan_count : 扫描结果行 * g_dev_count+g_scan_count+1 : SCAN 按钮 * g_dev_count+g_scan_count+2 : SELECT 按钮 */ #define DEV_ITEM_SCAN_BTN (-1) #define DEV_ITEM_SEL_BTN (-2) /* ---- 小工具 ---- */ static int s_strlen(const char *s) { int n = 0; while (s[n]) n++; return n; } static void s_strcpy(char *d, const char *s, int cap) { int i = 0; for (; i < cap - 1 && s[i]; i++) d[i] = s[i]; d[i] = 0; } static int s_streq(const char *a, const char *b) { while (*a && *b) { if (*a != *b) return 0; a++; b++; } return *a == *b; } static int s_streqn(const char *a, const char *b, int n) { for (int i = 0; i < n; i++) { if (a[i] != b[i]) return 0; } return 1; } static void set_status(const char *msg, u32 color) { s_strcpy(g_status, msg, sizeof(g_status)); g_status_color = color; } /* int -> 字符串 */ static int s_itoa(int v, char *out) { if (v == 0) { out[0] = '0'; out[1] = 0; return 1; } int neg = 0; if (v < 0) { neg = 1; v = -v; } char tmp[12]; int tl = 0; while (v > 0) { tmp[tl++] = (char)('0' + v % 10); v /= 10; } int p = 0; if (neg) out[p++] = '-'; while (tl > 0) out[p++] = tmp[--tl]; out[p] = 0; return p; } /* MAC 地址格式化: "XX:XX:XX:XX:XX:XX" */ static void fmt_mac(const u8 mac[6], char *out) { static const char h[] = "0123456789ABCDEF"; int p = 0; for (int i = 0; i < 6; i++) { if (i) out[p++] = ':'; out[p++] = h[(mac[i] >> 4) & 0xF]; out[p++] = h[mac[i] & 0xF]; } out[p] = 0; } /* 安全类型名称 */ static const char *sec_name(u8 sec) { switch (sec) { case NM_SEC_OPEN: return "Open"; case NM_SEC_WEP: return "WEP"; case NM_SEC_WPA: return "WPA"; case NM_SEC_WPA2: return "WPA2"; case NM_SEC_WPA3: return "WPA3"; default: return "?"; } } /* ---- INI 解析(填充字段值) ---- */ static void ini_fill_fields(const char *text, sfield *fields, int count) { char cur_sec[32]; cur_sec[0] = 0; const char *p = text; while (*p) { /* 取一行 */ const char *ls = p; while (*p && *p != '\n') p++; int len = (int)(p - ls); if (*p == '\n') p++; /* 去尾部 \r */ while (len > 0 && ls[len-1] == '\r') len--; /* 跳过前导空白 */ int i = 0; while (i < len && (ls[i] == ' ' || ls[i] == '\t')) i++; if (i >= len) continue; if (ls[i] == '#' || ls[i] == ';') continue; if (ls[i] == '[') { int si = 0; i++; while (i < len && ls[i] != ']' && si < 31) cur_sec[si++] = ls[i++]; cur_sec[si] = 0; continue; } /* key=value */ int eq = -1; for (int j = i; j < len; j++) if (ls[j] == '=') { eq = j; break; } if (eq < 0) continue; int klen = eq - i; /* key 去尾部空白 */ while (klen > 0 && (ls[i+klen-1] == ' ' || ls[i+klen-1] == '\t')) klen--; int vs = eq + 1; while (vs < len && (ls[vs] == ' ' || ls[vs] == '\t')) vs++; int vlen = len - vs; for (int f = 0; f < count; f++) { int skl = s_strlen(fields[f].section); int fkl = s_strlen(fields[f].key); if (skl != s_strlen(cur_sec) || !s_streqn(cur_sec, fields[f].section, skl)) continue; if (fkl != klen || !s_streqn(ls + i, fields[f].key, fkl)) continue; int n = vlen < 47 ? vlen : 47; for (int k = 0; k < n; k++) fields[f].value[k] = ls[vs + k]; fields[f].value[n] = 0; fields[f].dirty = 0; } } } /* ---- FUCK 序列化(行保留:替换已暴露键的值,缺失键追加到末尾) ---- */ static u32 fuck_serialize(void) { char *out = g_fuck_out; u32 olen = 0; u8 appended[KFIELD_COUNT]; for (int i = 0; i < (int)KFIELD_COUNT; i++) appended[i] = 0; char cur_sec[32]; cur_sec[0] = 0; const char *p = g_fuck_text; const char *end = g_fuck_text + g_fuck_len; while (p < end) { const char *ls = p; while (p < end && *p != '\n') p++; int len = (int)(p - ls); if (p < end && *p == '\n') p++; int wlen = len; /* 去 \r 用于解析 */ while (wlen > 0 && ls[wlen-1] == '\r') wlen--; int replaced = 0; int i = 0; while (i < wlen && (ls[i] == ' ' || ls[i] == '\t')) i++; if (i < wlen && ls[i] == '[') { int si = 0; i++; while (i < wlen && ls[i] != ']' && si < 31) cur_sec[si++] = ls[i++]; cur_sec[si] = 0; } else if (i < wlen && ls[i] != '#' && ls[i] != ';') { int eq = -1; for (int j = i; j < wlen; j++) if (ls[j] == '=') { eq = j; break; } if (eq >= 0) { int klen = eq - i; while (klen > 0 && (ls[i+klen-1] == ' ' || ls[i+klen-1] == '\t')) klen--; for (int f = 0; f < (int)KFIELD_COUNT; f++) { int skl = s_strlen(g_kfields[f].section); int fkl = s_strlen(g_kfields[f].key); if (skl != s_strlen(cur_sec) || !s_streqn(cur_sec, g_kfields[f].section, skl)) continue; if (fkl != klen || !s_streqn(ls + i, g_kfields[f].key, fkl)) continue; /* 替换为 key=value */ int vl = s_strlen(g_kfields[f].value); if (olen + (u32)fkl + 1 + (u32)vl + 1 < sizeof(g_fuck_out)) { for (int k = 0; k < fkl; k++) out[olen++] = g_kfields[f].key[k]; out[olen++] = '='; for (int k = 0; k < vl; k++) out[olen++] = g_kfields[f].value[k]; out[olen++] = '\n'; } appended[f] = 1; replaced = 1; break; } } } if (!replaced) { /* 原样保留(含换行) */ int raw = (int)(p - ls); if (olen + (u32)raw < sizeof(g_fuck_out)) { for (int k = 0; k < raw; k++) out[olen++] = ls[k]; } } } /* 未在原文出现的 dirty 字段 -> 末尾追加(ini_get 首匹配生效,缺失键无冲突) */ for (int f = 0; f < (int)KFIELD_COUNT; f++) { if (appended[f] || !g_kfields[f].dirty) continue; out[olen++] = '\n'; out[olen++] = '['; for (const char *s = g_kfields[f].section; *s; ) out[olen++] = *s++; out[olen++] = ']'; out[olen++] = '\n'; for (const char *s = g_kfields[f].key; *s; ) out[olen++] = *s++; out[olen++] = '='; for (const char *s = g_kfields[f].value; *s; ) out[olen++] = *s++; out[olen++] = '\n'; } return olen; } /* ---- NETCONF 序列化(全量重写,格式与 netman 解析器一致) ---- */ static u32 conf_serialize(void) { static const char *order[] = { "mode", "device", "name", "password", "ip", "gateway", "dns" }; u32 olen = 0; char *out = g_conf_out; for (int o = 0; o < 7; o++) { const char *k = order[o]; const char *v = ""; for (int f = 0; f < (int)NFIELD_COUNT; f++) if (s_streq(g_nfields[f].key, k)) { v = g_nfields[f].value; break; } const char *pfx = "network."; for (const char *s = pfx; *s; ) out[olen++] = *s++; for (const char *s = k; *s; ) out[olen++] = *s++; out[olen++] = '='; for (const char *s = v; *s; ) out[olen++] = *s++; out[olen++] = '\n'; } return olen; } /* ---- 磁盘加载 / 保存 ---- */ static void load_configs(void) { u8 *data = 0; u32 size = 0; g_fuck_loaded = 0; if (f32_read_path_lfn("system/deshab64/FUCK", &data, &size) == 0 || f32_read_path_lfn("FUCK", &data, &size) == 0) { if (size >= sizeof(g_fuck_text)) size = sizeof(g_fuck_text) - 1; for (u32 i = 0; i < size; i++) g_fuck_text[i] = (char)data[i]; g_fuck_text[size] = 0; g_fuck_len = size; g_fuck_loaded = 1; ini_fill_fields(g_fuck_text, g_kfields, (int)KFIELD_COUNT); } else { g_fuck_len = 0; g_fuck_text[0] = 0; } g_net_loaded = 0; if (f32_read_root_file("NETCONF CNF", &data, &size) == 0) { if (size >= sizeof(g_conf_text)) size = sizeof(g_conf_text) - 1; for (u32 i = 0; i < size; i++) g_conf_text[i] = (char)data[i]; g_conf_text[size] = 0; g_net_loaded = 1; ini_fill_fields(g_conf_text, g_nfields, (int)NFIELD_COUNT); } else { g_conf_text[0] = 0; } if (g_fuck_loaded && g_net_loaded) set_status("Loaded FUCK + NETCONF.CNF", DA_SUCCESS); else if (g_fuck_loaded) set_status("Loaded FUCK (NETCONF.CNF missing, defaults shown)", DA_WARNING); else set_status("Config files not found, defaults shown", DA_WARNING); } static void save_configs(void) { int ok_k = 0, ok_n = 0; if (g_page == 0) { u32 olen = fuck_serialize(); if (f32_write_path_lfn("system/deshab64/FUCK", (const u8 *)g_fuck_out, olen) == 0 || f32_write_path_lfn("FUCK", (const u8 *)g_fuck_out, olen) == 0) { ok_k = 1; for (int f = 0; f < (int)KFIELD_COUNT; f++) g_kfields[f].dirty = 0; /* 以新内容作为后续编辑基准 */ if (olen < sizeof(g_fuck_text)) { for (u32 i = 0; i < olen; i++) g_fuck_text[i] = g_fuck_out[i]; g_fuck_len = olen; g_fuck_text[olen] = 0; g_fuck_loaded = 1; } } set_status(ok_k ? "FUCK saved (reboot to apply)" : "FUCK save FAILED", ok_k ? DA_SUCCESS : DA_ERROR); } else { /* page 1 (NETWORK) 和 page 2 (DEVICES) 都保存 NETCONF.CNF */ u32 olen = conf_serialize(); if (f32_write_root_file("NETCONF CNF", (const u8 *)g_conf_out, olen) == 0) { ok_n = 1; for (int f = 0; f < (int)NFIELD_COUNT; f++) g_nfields[f].dirty = 0; } set_status(ok_n ? "NETCONF.CNF saved (reboot to apply)" : "NETCONF save FAILED", ok_n ? DA_SUCCESS : DA_ERROR); } } /* ---- 字段操作 ---- */ static sfield *cur_fields(void) { return g_page == 0 ? g_kfields : g_nfields; } static int cur_count(void) { if (g_page == 2) { /* 设备页: 设备数 + 扫描结果数 + 2 按钮 */ return g_dev_count + g_scan_count + 2; } return g_page == 0 ? (int)KFIELD_COUNT : (int)NFIELD_COUNT; } static void field_cycle(sfield *f, int dir) { if (f->type == FT_BOOL) { f->value[0] = (f->value[0] == '1') ? '0' : '1'; f->value[1] = 0; } else if (f->type == FT_INT) { int v = 0; for (const char *s = f->value; *s >= '0' && *s <= '9'; s++) v = v * 10 + (*s - '0'); v += dir; if (v > f->max) v = f->min; if (v < f->min) v = f->max; int n = 0; if (v == 0) f->value[n++] = '0'; char tmp[12]; int tl = 0; while (v > 0) { tmp[tl++] = (char)('0' + v % 10); v /= 10; } while (tl > 0) f->value[n++] = tmp[--tl]; f->value[n] = 0; } else if (f->type == FT_CHOICE) { /* 在 "a|b|c" 中找当前项,切到下一项/上一项 */ char opts[8][16]; int oc = 0; const char *s = f->choices; while (*s && oc < 8) { int l = 0; while (*s && *s != '|' && l < 15) opts[oc][l++] = *s++; opts[oc][l] = 0; oc++; if (*s == '|') s++; } int cur = -1; for (int i = 0; i < oc; i++) if (s_streq(f->value, opts[i])) { cur = i; break; } cur += dir; if (cur >= oc) cur = 0; if (cur < 0) cur = oc - 1; if (oc > 0) s_strcpy(f->value, opts[cur < 0 ? 0 : cur], sizeof(f->value)); } f->dirty = 1; } /* ---- Net API 初始化 ---- */ static void init_net_api(const da_boot_context *ctx) { g_net_device_count = 0; g_net_device_info = 0; g_net_scan_start = 0; g_net_scan_count = 0; g_net_scan_result = 0; g_net_is_wireless = 0; g_net_avail = 0; u64 api = ctx->dkm_kernel_api; if (!api) return; u64 net_api = *(u64 *)(api + 0x48); if (!net_api) return; g_net_device_count = (net_device_count_fn)*(u64 *)(net_api + 8); g_net_device_info = (net_device_info_fn)*(u64 *)(net_api + 16); g_net_scan_start = (net_scan_start_fn)*(u64 *)(net_api + 40); g_net_scan_count = (net_scan_count_fn)*(u64 *)(net_api + 48); g_net_scan_result = (net_scan_result_fn)*(u64 *)(net_api + 56); g_net_is_wireless = (net_is_wireless_fn)*(u64 *)(net_api + 64); g_net_avail = 1; } /* ---- 设备枚举 ---- */ static void query_devices(void) { g_dev_count = 0; if (!g_net_device_count || !g_net_device_info) return; u32 count = g_net_device_count(); if (count > DEV_MAX) count = DEV_MAX; for (u32 i = 0; i < count; i++) { nm_device_info info; da_memset(&info, 0, sizeof(info)); if (g_net_device_info(i, &info) != 0) continue; /* 复制到本地 */ g_devs[g_dev_count] = info; g_dev_count++; } } /* ---- WiFi 扫描 ---- */ static void trigger_scan(void) { if (!g_net_avail || !g_net_is_wireless || !g_net_scan_start) { set_status("Net API or WiFi scan not available", DA_ERROR); return; } if (g_sel < 0 || g_sel >= g_dev_count) { /* 如果当前不在设备行,尝试用上次选中的设备 */ if (g_dev_count == 0) { set_status("No devices", DA_ERROR); return; } } /* 找到选中的无线设备 */ int dev_idx = g_sel; if (dev_idx >= g_dev_count) dev_idx = 0; int wl = g_net_is_wireless(dev_idx); if (!wl) { /* 搜索第一个无线设备 */ int found = -1; for (int i = 0; i < g_dev_count; i++) { if (g_net_is_wireless(i)) { found = i; break; } } if (found < 0) { set_status("No wireless device found", DA_WARNING); return; } dev_idx = found; } set_status("Scanning WiFi...", DA_ACCENT_LIGHT); int rc = g_net_scan_start(dev_idx); if (rc != 0) { set_status("WiFi scan failed", DA_ERROR); return; } g_scan_count = 0; if (g_net_scan_count) { int sc = g_net_scan_count(dev_idx); if (sc > SCAN_MAX) sc = SCAN_MAX; for (int i = 0; i < sc; i++) { if (g_net_scan_result(dev_idx, i, &g_scans[g_scan_count]) == 0) g_scan_count++; } } g_scan_dev_idx = dev_idx; char msg[64]; s_strcpy(msg, "Scan found ", sizeof(msg)); char nb[12]; s_itoa(g_scan_count, nb); int ml = s_strlen(msg); for (int i = 0; nb[i] && ml < 60; i++) msg[ml++] = nb[i]; const char *suf = " networks"; for (const char *s = suf; *s && ml < 62; ) msg[ml++] = *s++; msg[ml] = 0; set_status(msg, g_scan_count > 0 ? DA_SUCCESS : DA_WARNING); } /* ---- 设备选择(写入 NETCONF.CNF device 字段) ---- */ static void select_device(int idx) { if (idx < 0 || idx >= g_dev_count) return; if (!g_devs[idx].name) return; s_strcpy(g_nfields[NF_DEVICE].value, g_devs[idx].name, sizeof(g_nfields[NF_DEVICE].value)); g_nfields[NF_DEVICE].dirty = 1; char msg[80]; s_strcpy(msg, "Device set to ", sizeof(msg)); int ml = s_strlen(msg); const char *nm = g_devs[idx].name; for (const char *s = nm; *s && ml < 78; ) msg[ml++] = *s++; const char *suf = " (F5 to save)"; for (const char *s = suf; *s && ml < 79; ) msg[ml++] = *s++; msg[ml] = 0; set_status(msg, DA_SUCCESS); } /* ---- SSID 选择(写入 NETCONF.CNF name 字段) ---- */ static void select_ssid(int idx) { if (idx < 0 || idx >= g_scan_count) return; s_strcpy(g_nfields[NF_NAME].value, g_scans[idx].ssid, sizeof(g_nfields[NF_NAME].value)); g_nfields[NF_NAME].dirty = 1; char msg[80]; s_strcpy(msg, "SSID set to ", sizeof(msg)); int ml = s_strlen(msg); for (const char *s = g_scans[idx].ssid; *s && ml < 78; ) msg[ml++] = *s++; const char *suf = " (F5 to save)"; for (const char *s = suf; *s && ml < 79; ) msg[ml++] = *s++; msg[ml] = 0; set_status(msg, DA_SUCCESS); } /* ---- 布局 ---- */ #define TAB_Y (DA_TITLEBAR_H + 4) #define TAB_H 26 #define TAB_W 130 #define FIELD_Y0 (TAB_Y + TAB_H + 10) #define ROW_H 24 #define LABEL_X 20 #define VALUE_X 320 #define VALUE_W 300 #define VALUE_H 20 #define BTN_Y_OFF 44 /* 底部按钮距状态栏高度 */ #define BTN_W 110 #define BTN_H 26 static int visible_rows(void) { int h = (int)g_ac.fb_h - FIELD_Y0 - BTN_Y_OFF - DA_STATUSBAR_H - 8; int r = h / ROW_H; return r < 1 ? 1 : r; } static void ensure_visible(void) { int vr = visible_rows(); if (g_sel < g_scroll) g_scroll = g_sel; if (g_sel >= g_scroll + vr) g_scroll = g_sel - vr + 1; } /* ---- 绘制 ---- */ static void draw_tab(int idx, const char *label, int x) { int active = (g_page == idx); u32 bg = active ? DA_BG_TERTIARY : DA_BG_SECONDARY; u32 fg = active ? DA_ACCENT_LIGHT : DA_TEXT_DIM; da_fill_rounded_rect(&g_ac, x, TAB_Y, TAB_W - 8, TAB_H, bg, 6); da_rect_outline(&g_ac, x, TAB_Y, TAB_W - 8, TAB_H, active ? DA_BORDER_FOCUS : DA_BORDER, 6); int lx = x + 14; da_draw_string(&g_ac, label, lx, TAB_Y + 4, fg, bg, 12); } static void draw_field_row(int fi, int row_y) { sfield *f = &cur_fields()[fi]; int selected = (fi == g_sel); i64 fw = (i64)g_ac.fb_w - 40; if (selected) da_fill_rect(&g_ac, LABEL_X - 4, row_y - 2, fw, ROW_H, DA_BG_SECONDARY); /* 标签: section/key */ char label[64]; int lp = 0; for (const char *s = f->section; *s && lp < 60; ) label[lp++] = *s++; label[lp++] = '/'; for (const char *s = f->key; *s && lp < 62; ) label[lp++] = *s++; label[lp] = 0; da_draw_string(&g_ac, label, LABEL_X, row_y + 1, selected ? DA_TEXT_PRIMARY : DA_TEXT_DIM, selected ? DA_BG_SECONDARY : DA_BG_PRIMARY, 12); /* 值框 */ u32 vbg = selected ? DA_BG_TERTIARY : DA_BG_SECONDARY; u32 vborder = selected ? DA_BORDER_FOCUS : DA_BORDER; da_fill_rect(&g_ac, VALUE_X, row_y, VALUE_W, VALUE_H, vbg); da_rect_outline(&g_ac, VALUE_X, row_y, VALUE_W, VALUE_H, vborder, 2); char disp[56]; int dp = 0; if (f->type == FT_BOOL || f->type == FT_CHOICE || f->type == FT_INT) { disp[dp++] = '<'; disp[dp++] = ' '; for (const char *s = f->value; *s && dp < 52; ) disp[dp++] = *s++; disp[dp++] = ' '; disp[dp++] = '>'; } else { for (const char *s = f->value; *s && dp < 52; ) disp[dp++] = *s++; } disp[dp] = 0; u32 vfg; if (f->type == FT_BOOL) vfg = (f->value[0] == '1') ? DA_SUCCESS : DA_ERROR; else vfg = DA_ACCENT_LIGHT; da_draw_string(&g_ac, disp, VALUE_X + 8, row_y + 1, vfg, vbg, 12); /* 文本字段编辑光标 */ if (selected && f->type == FT_TEXT) { int vl = s_strlen(f->value); i64 cx = VALUE_X + 8 + (i64)vl * 12; if (cx < VALUE_X + VALUE_W - 12) da_fill_rect(&g_ac, cx, row_y + VALUE_H - 4, 11, 2, DA_ACCENT); } /* dirty 标记 */ if (f->dirty) da_draw_char(&g_ac, '*', VALUE_X + VALUE_W + 6, row_y + 1, DA_WARNING, selected ? DA_BG_SECONDARY : DA_BG_PRIMARY); } /* ---- 设备页绘制 ---- */ static int dev_item_count(void) { return g_dev_count + g_scan_count + 2; } /* 返回设备页第 sel 项的类型和索引 */ static void dev_item_info(int sel, int *type, int *idx) { if (sel < g_dev_count) { *type = 0; *idx = sel; return; } /* 设备 */ int scan_base = g_dev_count + 1; /* +1 跳过分隔线 */ if (sel >= scan_base && sel < scan_base + g_scan_count) { *type = 1; *idx = sel - scan_base; return; } /* 扫描 */ int btn_base = scan_base + g_scan_count; if (sel == btn_base) { *type = 2; *idx = 0; return; } /* SCAN */ if (sel == btn_base + 1) { *type = 3; *idx = 0; return; } /* SELECT */ *type = -1; *idx = -1; } static void draw_device_row(int idx, int row_y, int selected) { nm_device_info *d = &g_devs[idx]; if (selected) da_fill_rect(&g_ac, LABEL_X - 4, row_y - 2, (i64)g_ac.fb_w - 40, ROW_H, DA_BG_SECONDARY); /* 索引 */ char idx_s[8]; s_itoa(idx, idx_s); da_draw_string(&g_ac, "[", LABEL_X, row_y + 1, selected ? DA_TEXT_PRIMARY : DA_TEXT_DIM, selected ? DA_BG_SECONDARY : DA_BG_PRIMARY, 12); da_draw_string(&g_ac, idx_s, LABEL_X + 12, row_y + 1, selected ? DA_TEXT_PRIMARY : DA_TEXT_DIM, selected ? DA_BG_SECONDARY : DA_BG_PRIMARY, 12); da_draw_string(&g_ac, "]", LABEL_X + 24, row_y + 1, selected ? DA_TEXT_PRIMARY : DA_TEXT_DIM, selected ? DA_BG_SECONDARY : DA_BG_PRIMARY, 12); /* 名称 */ int nx = LABEL_X + 40; const char *nm = d->name ? d->name : ""; u32 name_fg = DA_ACCENT_LIGHT; if (d->flags & NM_NET_F_WIRELESS) name_fg = DA_WARNING; da_draw_string(&g_ac, nm, nx, row_y + 1, name_fg, selected ? DA_BG_SECONDARY : DA_BG_PRIMARY, 12); /* MAC */ char mac_s[20]; fmt_mac(d->mac, mac_s); int mac_x = LABEL_X + 200; da_draw_string(&g_ac, mac_s, mac_x, row_y + 1, DA_TEXT_DIM, selected ? DA_BG_SECONDARY : DA_BG_PRIMARY, 12); /* 状态标志 */ int sx = mac_x + 180; if (d->flags & NM_NET_F_LINK_UP) da_draw_string(&g_ac, "LINK", sx, row_y + 1, DA_SUCCESS, selected ? DA_BG_SECONDARY : DA_BG_PRIMARY, 12); else da_draw_string(&g_ac, "down", sx, row_y + 1, DA_ERROR, selected ? DA_BG_SECONDARY : DA_BG_PRIMARY, 12); sx += 60; if (d->flags & NM_NET_F_TX_READY) da_draw_string(&g_ac, "TX", sx, row_y + 1, DA_SUCCESS, selected ? DA_BG_SECONDARY : DA_BG_PRIMARY, 12); sx += 30; if (d->flags & NM_NET_F_RX_READY) da_draw_string(&g_ac, "RX", sx, row_y + 1, DA_SUCCESS, selected ? DA_BG_SECONDARY : DA_BG_PRIMARY, 12); sx += 30; if (d->flags & NM_NET_F_WIRELESS) da_draw_string(&g_ac, "WiFi", sx, row_y + 1, DA_WARNING, selected ? DA_BG_SECONDARY : DA_BG_PRIMARY, 12); /* 当前选中设备标记 */ if (d->name && s_streq(g_nfields[NF_DEVICE].value, d->name)) da_draw_char(&g_ac, '*', (i64)g_ac.fb_w - 30, row_y + 1, DA_SUCCESS, selected ? DA_BG_SECONDARY : DA_BG_PRIMARY); } static void draw_scan_row(int idx, int row_y, int selected) { nm_scan_result *r = &g_scans[idx]; if (selected) da_fill_rect(&g_ac, LABEL_X - 4, row_y - 2, (i64)g_ac.fb_w - 40, ROW_H, DA_BG_SECONDARY); /* SSID */ const char *ssid = r->ssid[0] ? r->ssid : ""; da_draw_string(&g_ac, ssid, LABEL_X, row_y + 1, selected ? DA_TEXT_PRIMARY : DA_ACCENT_LIGHT, selected ? DA_BG_SECONDARY : DA_BG_PRIMARY, 12); /* RSSI */ char rssi_s[12]; rssi_s[0] = 0; s_itoa(r->rssi, rssi_s); int rx = LABEL_X + 260; da_draw_string(&g_ac, rssi_s, rx, row_y + 1, r->rssi > -50 ? DA_SUCCESS : (r->rssi > -70 ? DA_WARNING : DA_ERROR), selected ? DA_BG_SECONDARY : DA_BG_PRIMARY, 12); da_draw_string(&g_ac, "dBm", rx + 40, row_y + 1, DA_TEXT_DIM, selected ? DA_BG_SECONDARY : DA_BG_PRIMARY, 12); /* 信道 */ char ch_s[8]; s_itoa(r->channel, ch_s); da_draw_string(&g_ac, "ch", rx + 90, row_y + 1, DA_TEXT_DIM, selected ? DA_BG_SECONDARY : DA_BG_PRIMARY, 12); da_draw_string(&g_ac, ch_s, rx + 110, row_y + 1, DA_TEXT_DIM, selected ? DA_BG_SECONDARY : DA_BG_PRIMARY, 12); /* 加密 */ da_draw_string(&g_ac, sec_name(r->security), rx + 140, row_y + 1, r->security == NM_SEC_OPEN ? DA_SUCCESS : DA_WARNING, selected ? DA_BG_SECONDARY : DA_BG_PRIMARY, 12); /* 当前选中 SSID 标记 */ if (r->ssid[0] && s_streq(g_nfields[NF_NAME].value, r->ssid)) da_draw_char(&g_ac, '*', (i64)g_ac.fb_w - 30, row_y + 1, DA_SUCCESS, selected ? DA_BG_SECONDARY : DA_BG_PRIMARY); } static void draw_device_button(int x, int y, const char *label, int selected) { u32 bg = selected ? DA_BG_TERTIARY : DA_BG_SECONDARY; u32 border = selected ? DA_BORDER_FOCUS : DA_BORDER; da_fill_rounded_rect(&g_ac, x, y, BTN_W, BTN_H, bg, 6); da_rect_outline(&g_ac, x, y, BTN_W, BTN_H, border, 6); int ll = s_strlen(label); da_draw_string(&g_ac, label, x + (BTN_W - ll * 12) / 2, y + 4, DA_ACCENT_LIGHT, bg, 12); } static void draw_device_page(void) { int vr = visible_rows(); int count = dev_item_count(); /* 列表 */ for (int r = 0; r < vr; r++) { int item = g_scroll + r; if (item >= count) break; int row_y = FIELD_Y0 + r * ROW_H; int type, idx; dev_item_info(item, &type, &idx); if (type == 0) { draw_device_row(idx, row_y, item == g_sel); } else if (type == 1) { draw_scan_row(idx, row_y, item == g_sel); } else if (type == 2) { draw_device_button(LABEL_X, row_y, "SCAN (S)", item == g_sel); } else if (type == 3) { draw_device_button(LABEL_X + BTN_W + 12, row_y, "SELECT", item == g_sel); } } /* 分隔线: 设备列表和扫描结果之间 */ int sep_item = g_dev_count; /* 分隔线位置 */ int sep_row = sep_item - g_scroll; if (sep_row >= 0 && sep_row < vr) { int sy = FIELD_Y0 + sep_row * ROW_H + ROW_H / 2; da_fill_rect(&g_ac, LABEL_X, sy, (i64)g_ac.fb_w - 40, 1, DA_BORDER); const char *hdr = (g_scan_count > 0) ? "--- WiFi Scan Results ---" : "--- WiFi Scan (press S) ---"; int hx = LABEL_X + 20; da_draw_string(&g_ac, hdr, hx, sy - 8, DA_TEXT_DIM, DA_BG_PRIMARY, 12); } /* 滚动指示 */ if (count > vr) { char info[32]; int ip = 0; int a = g_scroll + 1, b = g_scroll + vr > count ? count : g_scroll + vr; char nb[12]; int nl; nl = 0; while (a) { nb[nl++] = (char)('0' + a % 10); a /= 10; } while (nl > 0) info[ip++] = nb[--nl]; info[ip++] = '-'; nl = 0; if (b == 0) nb[nl++] = '0'; while (b) { nb[nl++] = (char)('0' + b % 10); b /= 10; } while (nl > 0) info[ip++] = nb[--nl]; info[ip++] = '/'; nl = 0; { int c2 = count; if (c2 == 0) nb[nl++] = '0'; while (c2) { nb[nl++] = (char)('0' + c2 % 10); c2 /= 10; } } while (nl > 0) info[ip++] = nb[--nl]; info[ip] = 0; da_draw_string(&g_ac, info, (i64)g_ac.fb_w - 120, TAB_Y + 6, DA_TEXT_DIM, DA_BG_PRIMARY, 12); } } static void draw_button(int x, int y, const char *label, int hot) { u32 bg = hot ? DA_BG_TERTIARY : DA_BG_SECONDARY; da_fill_rounded_rect(&g_ac, x, y, BTN_W, BTN_H, bg, 6); da_rect_outline(&g_ac, x, y, BTN_W, BTN_H, DA_BORDER_FOCUS, 6); int ll = s_strlen(label); da_draw_string(&g_ac, label, x + (BTN_W - ll * 12) / 2, y + 4, DA_ACCENT_LIGHT, bg, 12); } static void redraw_all(void) { da_cursor_restore(&g_ac, &g_cursor); da_fill_bg(&g_ac, DA_BG_PRIMARY); da_draw_titlebar(&g_ac, "Settings", (i64)g_ac.fb_w); draw_tab(0, "KERNEL", 16); draw_tab(1, "NETWORK", 16 + TAB_W); draw_tab(2, "DEVICES", 16 + 2 * TAB_W); if (g_page < 2) { /* 字段列表 */ int vr = visible_rows(); int count = cur_count(); for (int r = 0; r < vr; r++) { int fi = g_scroll + r; if (fi >= count) break; draw_field_row(fi, FIELD_Y0 + r * ROW_H); } /* 滚动指示 */ if (count > vr) { char info[32]; int ip = 0; int a = g_scroll + 1, b = g_scroll + vr > count ? count : g_scroll + vr; char nb[12]; int nl; nl = 0; while (a) { nb[nl++] = (char)('0' + a % 10); a /= 10; } while (nl > 0) info[ip++] = nb[--nl]; info[ip++] = '-'; nl = 0; if (b == 0) nb[nl++] = '0'; while (b) { nb[nl++] = (char)('0' + b % 10); b /= 10; } while (nl > 0) info[ip++] = nb[--nl]; info[ip++] = '/'; nl = 0; { int c2 = count; if (c2 == 0) nb[nl++] = '0'; while (c2) { nb[nl++] = (char)('0' + c2 % 10); c2 /= 10; } } while (nl > 0) info[ip++] = nb[--nl]; info[ip] = 0; da_draw_string(&g_ac, info, (i64)g_ac.fb_w - 120, TAB_Y + 6, DA_TEXT_DIM, DA_BG_PRIMARY, 12); } } else { /* 设备页 */ if (!g_net_avail) { da_draw_string(&g_ac, "Net API not available (no kernel network support)", LABEL_X, FIELD_Y0, DA_WARNING, DA_BG_PRIMARY, 12); } else if (g_dev_count == 0) { da_draw_string(&g_ac, "No network devices found", LABEL_X, FIELD_Y0, DA_WARNING, DA_BG_PRIMARY, 12); } else { draw_device_page(); } } /* 底部按钮 + 状态消息 */ int by = (int)g_ac.fb_h - DA_STATUSBAR_H - BTN_Y_OFF + 8; draw_button(20, by, "SAVE (F5)", 0); draw_button(20 + BTN_W + 12, by, "RELOAD", 0); if (g_status[0]) da_draw_string(&g_ac, g_status, 20 + 2 * (BTN_W + 12) + 8, by + 4, g_status_color, DA_BG_PRIMARY, 12); /* 状态栏 */ const char *hint; if (g_page == 0) hint = "F1 Kernel | F2 Network | F3 Devices | F5 Save | F6 Reload | Esc Quit | reboot to apply"; else if (g_page == 1) hint = "F1 Kernel | F2 Network | F3 Devices | F5 Save | F6 Reload | Esc Quit | netman reads at boot"; else hint = "F1 Kernel | F2 Network | F3 Devices | S Scan | Enter Select | F5 Save | Esc Quit"; da_draw_statusbar(&g_ac, hint, (i64)g_ac.fb_w, (i64)g_ac.fb_h); da_cursor_save(&g_ac, &g_cursor); da_cursor_draw(&g_ac, &g_cursor, DA_CURSOR_COLOR); } /* ---- 页面切换 ---- */ static void switch_page(int p) { if (p == g_page) return; g_sel_saved[g_page] = g_sel; g_scroll_saved[g_page] = g_scroll; g_page = p; g_sel = g_sel_saved[p]; g_scroll = g_scroll_saved[p]; ensure_visible(); } /* ---- 鼠标点击 ---- */ static void handle_click(int mx, int my) { /* tab 区 */ if (my >= TAB_Y && my < TAB_Y + TAB_H) { if (mx >= 16 && mx < 16 + TAB_W - 8) { switch_page(0); return; } if (mx >= 16 + TAB_W && mx < 16 + 2 * TAB_W - 8) { switch_page(1); return; } if (mx >= 16 + 2 * TAB_W && mx < 16 + 3 * TAB_W - 8) { switch_page(2); return; } } if (g_page < 2) { /* 字段区 */ if (my >= FIELD_Y0) { int row = (my - FIELD_Y0) / ROW_H; int fi = g_scroll + row; if (fi >= 0 && fi < cur_count() && row < visible_rows()) { g_sel = fi; sfield *f = &cur_fields()[fi]; if (mx >= VALUE_X && mx < VALUE_X + VALUE_W && f->type != FT_TEXT) field_cycle(f, 1); return; } } } else { /* 设备页 */ if (my >= FIELD_Y0) { int row = (my - FIELD_Y0) / ROW_H; int item = g_scroll + row; if (item >= 0 && item < dev_item_count() && row < visible_rows()) { g_sel = item; int type, idx; dev_item_info(item, &type, &idx); if (type == 0) { select_device(idx); } else if (type == 1) { select_ssid(idx); } else if (type == 2) { trigger_scan(); } else if (type == 3) { /* SELECT 按钮: 选择当前选中设备 */ if (g_sel < g_dev_count) select_device(g_sel); } return; } } } /* 按钮区 */ int by = (int)g_ac.fb_h - DA_STATUSBAR_H - BTN_Y_OFF + 8; if (my >= by && my < by + BTN_H) { if (mx >= 20 && mx < 20 + BTN_W) { save_configs(); return; } if (mx >= 20 + BTN_W + 12 && mx < 20 + 2 * BTN_W + 12) { load_configs(); if (g_page == 2) query_devices(); return; } } } /* ---- 主入口 ---- */ __attribute__((visibility("default"))) void dsk_entry(const da_boot_context *ctx) { __asm__ volatile("cli"); da_slog("settings", "boot"); if (!ctx || ctx->magic != DA_BOOT_MAGIC) { da_slog("settings", "bad context"); for (;;) __asm__("hlt"); } da_init(&g_ac, ctx); if (!g_ac.block_read || !g_ac.block_write) { da_slog("settings", "block api missing"); for (;;) __asm__("hlt"); } f32_init((f32_block_read_fn)g_ac.block_read, (f32_block_write_fn)g_ac.block_write); /* 初始化 Net API */ init_net_api(ctx); if (g_net_avail) query_devices(); g_status[0] = 0; load_configs(); da_mouse_init(); g_mouse.idx = 0; g_mouse.has_pkt = 0; da_cursor_init(&g_cursor, (i64)g_ac.fb_w, (i64)g_ac.fb_h); int shift = 0, e0 = 0; int prev_btn = 0; redraw_all(); for (;;) { /* 鼠标 */ if (da_mouse_poll(&g_mouse, &g_cursor, (i64)g_ac.fb_w, (i64)g_ac.fb_h)) { if (g_cursor.btn && !prev_btn) handle_click(g_cursor.mx, g_cursor.my); prev_btn = g_cursor.btn; redraw_all(); continue; } /* 键盘 */ u8 st = inb(0x64); if (!(st & 1)) { __asm__("pause"); continue; } if (st & 0x20) continue; /* AUX 数据留给鼠标路径 */ u8 sc = inb(0x60); if (sc == 0xE0) { e0 = 1; continue; } if (sc == 0x2A || sc == 0x36) { shift = 1; continue; } if (sc == 0xAA || sc == 0xB6) { shift = 0; continue; } if (sc & 0x80) { e0 = 0; continue; } if (sc == 0x01 && !e0) { /* Esc */ da_slog("settings", "exit"); da_cursor_restore(&g_ac, &g_cursor); return; } if (!e0 && sc == 0x3B) { switch_page(0); redraw_all(); continue; } /* F1 */ if (!e0 && sc == 0x3C) { switch_page(1); redraw_all(); continue; } /* F2 */ if (!e0 && sc == 0x3D) { switch_page(2); redraw_all(); continue; } /* F3 */ if (!e0 && sc == 0x0F) { switch_page((g_page + 1) % 3); redraw_all(); continue; } /* Tab */ if (!e0 && sc == 0x3F) { save_configs(); redraw_all(); continue; } /* F5 */ if (!e0 && sc == 0x40) { load_configs(); if (g_page == 2) query_devices(); redraw_all(); continue; } /* F6 */ int count = cur_count(); if (e0) { if (sc == 0x48) { /* Up */ if (g_sel > 0) g_sel--; ensure_visible(); } else if (sc == 0x50) { /* Down */ if (g_sel < count - 1) g_sel++; ensure_visible(); } else if (sc == 0x4B) { /* Left */ if (g_page < 2) { sfield *f = &cur_fields()[g_sel]; if (f->type != FT_TEXT) field_cycle(f, -1); } } else if (sc == 0x4D) { /* Right */ if (g_page < 2) { sfield *f = &cur_fields()[g_sel]; if (f->type != FT_TEXT) field_cycle(f, 1); } } else if (sc == 0x47) { g_sel = 0; ensure_visible(); } /* Home */ else if (sc == 0x4F) { g_sel = count - 1; ensure_visible(); } /* End */ else if (sc == 0x49) { /* PgUp */ g_sel -= visible_rows(); if (g_sel < 0) g_sel = 0; ensure_visible(); } else if (sc == 0x51) { /* PgDn */ g_sel += visible_rows(); if (g_sel > count - 1) g_sel = count - 1; ensure_visible(); } e0 = 0; redraw_all(); continue; } /* 设备页特殊键 */ if (g_page == 2) { if (sc == 0x1C || sc == 0x39) { /* Enter / Space */ int type, idx; dev_item_info(g_sel, &type, &idx); if (type == 0) select_device(idx); else if (type == 1) select_ssid(idx); else if (type == 2) trigger_scan(); else if (type == 3) { if (g_sel < g_dev_count) select_device(g_sel); } redraw_all(); continue; } /* 'S' 键扫描 */ if (sc == 0x1F) { /* S */ trigger_scan(); redraw_all(); continue; } /* 其他键在设备页不处理 */ continue; } /* 字段页键盘输入 */ sfield *f = &cur_fields()[g_sel]; if (sc == 0x1C || sc == 0x39) { /* Enter / Space */ if (f->type != FT_TEXT) field_cycle(f, 1); redraw_all(); continue; } if (sc == 0x0E) { /* Backspace */ if (f->type == FT_TEXT) { int l = s_strlen(f->value); if (l > 0) { f->value[l-1] = 0; f->dirty = 1; } } redraw_all(); continue; } char c = da_scan_to_ascii(sc, shift); if (c && c >= 32 && c <= 126 && f->type == FT_TEXT) { int l = s_strlen(f->value); if (l < 46) { f->value[l] = c; f->value[l+1] = 0; f->dirty = 1; } redraw_all(); continue; } } }