From b780a6b868f666f8b3c1bdc42b9f20acbcd98a16 Mon Sep 17 00:00:00 2001 From: Linehu Date: Tue, 6 Oct 2026 13:51:00 +0800 Subject: [PATCH] feat(desktop): taskbar wifi quick card - scan list / password / connect via net api - taskbar.c: wifi popup card anchored above tray net icon; net api slots read via kernel_api+0x48 hard offsets (UTSM net.h layout, connect@0x48) - flow: icon click -> scan_start -> per-frame scan_count/result pull -> SSID list (security tag + rssi bars) -> open net direct connect / secured net password textbox (scan set 1 -> ascii, enter=connect) -> net_api.connect - no wireless device in QEMU: graceful "not detected" status, no panic (by design: wireless dev absence is attest-exempt, not a stub) - shell.c: handle_key routes to wifi_key when card open (modal) - sdk kernel_api.h: mirror UTSM dkm_net_api layout (append connect/disconnect before device_stats, fixes slot offset mismatch) - QEMU verified: card opens, renders, graceful no-device path (serial "[wifi] no wireless device") --- CODE/desktop/desktop.h | 6 +- CODE/desktop/shell.c | 3 + CODE/desktop/taskbar.c | 349 ++++++++++++++++++++++++++- CODE/sdk/include/deshab/kernel_api.h | 5 + 4 files changed, 360 insertions(+), 3 deletions(-) diff --git a/CODE/desktop/desktop.h b/CODE/desktop/desktop.h index 008dab8..7729979 100644 --- a/CODE/desktop/desktop.h +++ b/CODE/desktop/desktop.h @@ -509,7 +509,11 @@ void draw_ctx_menu(void); void taskbar_draw(void); int taskbar_click(int mx, int my); int taskbar_init(int (*read_to)(const char *, unsigned char *, - unsigned int, unsigned int *)); /* M2: logo 纹理 */ + unsigned int, unsigned int *)); +/* ---- taskbar.c WiFi 快捷卡片 ---- */ +extern int g_wifi_open; /* 卡片是否展开 */ +int wifi_key(u8 sc, int shift); /* shell.c handle_key 路由;1=已消费 */ +/* M2: logo 纹理 */ extern int g_tb2_start_rect[4]; extern int g_tb2_tv_rect[4]; extern int g_tb2_app_rect[MAX_WINDOWS][4]; diff --git a/CODE/desktop/shell.c b/CODE/desktop/shell.c index dc82947..f0c9e72 100644 --- a/CODE/desktop/shell.c +++ b/CODE/desktop/shell.c @@ -478,6 +478,9 @@ int handle_key(u8 sc) { if (sc == 0xB8) { g_alt = 0; return 0; } if (sc & 0x80) { g_e0 = 0; return 0; } /* 其他释放码忽略 */ + /* WiFi 快捷卡片(模态:Esc 收起 / 密码模式接管输入) */ + if (g_wifi_open) return wifi_key(sc, g_shift); + /* Esc:关闭 overlay */ if (sc == 0x01) { if (g_taskview) { g_taskview = 0; return 1; } diff --git a/CODE/desktop/taskbar.c b/CODE/desktop/taskbar.c index bde0c0f..7c8450a 100644 --- a/CODE/desktop/taskbar.c +++ b/CODE/desktop/taskbar.c @@ -1,4 +1,4 @@ -/* taskbar.c — Win11 Fluent 居中悬浮任务栏 + 系统托盘(M2) +/* taskbar.c — Win11 Fluent 居中悬浮任务栏 + 系统托盘(M2) * * 布局(深色主题 g_wf_dark): * 主任务栏:48px 高,距底 8px,水平居中悬浮,acrylic 圆角 12 @@ -33,6 +33,343 @@ int g_tb2_clk_rect[4]; #define TB_PAD 8 /* 条内边距 */ static const wf_theme *tb_theme(void) { return &g_wf_light; } +static wf_btn_state tb_hover_state(const int r[4]); + +/* ============================================================ + * WiFi 快捷卡片(托盘网络图标弹出:扫描列表 → 密码 → 连接) + * + * net API 经 g_kernel_api + 0x48(DSB_KAPI_OFF_NET)取得, + * dkm_net_api 槽位按 UTSM include/utsm/net.h 布局(每槽 8 字节): + * 0x00 register_device 0x08 device_count 0x10 device_info + * 0x18 tx 0x20 rx_poll + * 0x28 scan_start 0x30 scan_count + * 0x38 scan_result 0x40 is_wireless + * 0x48 connect 0x50 disconnect 0x58 device_stats + * ============================================================ */ + +#define WIFI_KAPI_OFF_NET 0x48 +#define WIFI_NETAPI_SCAN_START 0x28 +#define WIFI_NETAPI_SCAN_COUNT 0x30 +#define WIFI_NETAPI_SCAN_RESULT 0x38 +#define WIFI_NETAPI_IS_WIRELESS 0x40 +#define WIFI_NETAPI_CONNECT 0x48 +#define WIFI_NETAPI_DEVICE_COUNT 0x08 + +#define WIFI_MAX_ROWS 8 /* 卡片内最多显示行数 */ +#define WIFI_PW_MAX 63 +#define WIFI_ROW_H 36 + +/* dkm_net_scan_result 镜像(布局必须与 UTSM net.h 一致,对齐 1 无填充) */ +typedef struct wifi_scan_item { + char ssid[33]; + u8 bssid[6]; + u8 channel; + signed char rssi; + u8 security; +} wifi_scan_item; + +typedef int (*wifi_fn_scan_start)(u32); +typedef int (*wifi_fn_scan_count)(u32); +typedef int (*wifi_fn_scan_result)(u32, u32, wifi_scan_item *); +typedef int (*wifi_fn_is_wireless)(u32); +typedef int (*wifi_fn_connect)(u32, const char *, const char *); +typedef u32 (*wifi_fn_device_count)(void); + +int g_wifi_open = 0; +static int g_wifi_mode; /* 0=列表 1=密码输入 */ +static int g_wifi_dev = -1; /* 无线设备索引,-1 = 无 */ +static int g_wifi_scanning; +static int g_wifi_n; /* 缓存的扫描结果数 */ +static int g_wifi_sel = -1; +static char g_wifi_sel_ssid[33]; +static u8 g_wifi_sel_sec; +static char g_wifi_pw[WIFI_PW_MAX + 1]; +static char g_wifi_status[64]; +static wifi_scan_item g_wifi_items[32]; + +static int g_wifi_card_rect[4]; +static int g_wifi_row_rect[WIFI_MAX_ROWS][4]; +static int g_wifi_rescan_rect[4]; +static int g_wifi_pwbox_rect[4]; +static int g_wifi_ok_rect[4]; +static int g_wifi_cancel_rect[4]; + +/* 取 net API 函数槽(kapi/netapi 均缺失时返回 0) */ +static void *wifi_slot(u32 slot_off) { + if (!g_kernel_api) return 0; + u64 net = *(u64 *)(g_kernel_api + WIFI_KAPI_OFF_NET); + if (!net) return 0; + u64 fn = *(u64 *)(net + slot_off); + return (void *)(unsigned long)fn; +} + +/* 找无线设备;返回 0 = 已定位 g_wifi_dev */ +static int wifi_dev_find(void) { + wifi_fn_device_count cnt = (wifi_fn_device_count)wifi_slot(WIFI_NETAPI_DEVICE_COUNT); + wifi_fn_is_wireless isw = (wifi_fn_is_wireless)wifi_slot(WIFI_NETAPI_IS_WIRELESS); + g_wifi_dev = -1; + if (!cnt || !isw) return -1; + u32 n = cnt(); + for (u32 i = 0; i < n; i++) { + if (isw(i) == 1) { g_wifi_dev = (int)i; return 0; } + } + return -1; +} + +/* 拉取当前扫描结果到缓存 */ +static void wifi_results_pull(void) { + wifi_fn_scan_count scnt = (wifi_fn_scan_count)wifi_slot(WIFI_NETAPI_SCAN_COUNT); + wifi_fn_scan_result sres = (wifi_fn_scan_result)wifi_slot(WIFI_NETAPI_SCAN_RESULT); + if (!scnt || !sres || g_wifi_dev < 0) return; + int n = scnt((u32)g_wifi_dev); + if (n < 0) n = 0; + if (n > 32) n = 32; + for (int i = 0; i < n; i++) + sres((u32)g_wifi_dev, (u32)i, &g_wifi_items[i]); + g_wifi_n = n; +} + +/* 打开卡片并踢一脚扫描 */ +static void wifi_card_open(void) { + g_wifi_open = 1; + g_wifi_mode = 0; + g_wifi_sel = -1; + g_wifi_pw[0] = 0; + g_wifi_status[0] = 0; + g_wifi_n = 0; + g_wifi_scanning = 1; + if (wifi_dev_find() != 0) { + g_wifi_scanning = 0; + const char *m = "未检测到无线网卡"; + int i = 0; + for (; m[i]; i++) g_wifi_status[i] = m[i]; + g_wifi_status[i] = 0; + slog("[wifi] no wireless device"); + return; + } + wifi_fn_scan_start ss = (wifi_fn_scan_start)wifi_slot(WIFI_NETAPI_SCAN_START); + if (!ss || ss((u32)g_wifi_dev) != 0) { + g_wifi_scanning = 0; + const char *m = "扫描启动失败"; + int i = 0; + for (; m[i]; i++) g_wifi_status[i] = m[i]; + g_wifi_status[i] = 0; + slog("[wifi] scan_start failed"); + } +} + +/* 发起连接(当前选中网络 + 密码) */ +static void wifi_connect_go(void) { + wifi_fn_connect conn = (wifi_fn_connect)wifi_slot(WIFI_NETAPI_CONNECT); + if (!conn || g_wifi_dev < 0) return; + const char *m = "连接中..."; + int i = 0; + for (; m[i]; i++) g_wifi_status[i] = m[i]; + g_wifi_status[i] = 0; + int rc = conn((u32)g_wifi_dev, g_wifi_sel_ssid, g_wifi_pw); + if (rc == 0) { + m = "已连接"; + g_wifi_mode = 0; + g_wifi_open = 0; /* 连接成功收起卡片 */ + } else { + m = "连接失败"; + } + for (i = 0; m[i]; i++) g_wifi_status[i] = m[i]; + g_wifi_status[i] = 0; + { + char buf[72]; + buf[0] = '['; buf[1] = 'w'; buf[2] = 'i'; buf[3] = 'f'; buf[4] = 'i'; buf[5] = ']'; buf[6] = ' '; + int p = 7; + for (i = 0; g_wifi_sel_ssid[i] && p < 60; i++) buf[p++] = g_wifi_sel_ssid[i]; + buf[p++] = ' '; buf[p++] = rc == 0 ? 'o' : 'f'; buf[p++] = ' '; buf[p++] = '0' + (rc == 0 ? 0 : 1); buf[p] = 0; + slog(buf); + } + g_wifi_pw[0] = 0; +} + +/* 卡片绘制(taskbar_draw 末尾调用;每次绘制顺带收割扫描结果) */ +static void wifi_draw(void) { + if (!g_wifi_open) return; + const wf_theme *t = tb_theme(); + int W = (int)g_fb_w; + + /* 扫描收割:scanning 时每帧查 count,结果稳定(>0)即认为出网 */ + if (g_wifi_scanning && g_wifi_dev >= 0) { + wifi_results_pull(); + if (g_wifi_n > 0) g_wifi_scanning = 0; + } + + const int CW = 300; + int rows = g_wifi_n; + if (rows > WIFI_MAX_ROWS) rows = WIFI_MAX_ROWS; + int ch; + if (g_wifi_mode == 1) ch = 46 + 30 + 48 + 44 + 26 + 10; + else ch = 46 + (rows ? rows * WIFI_ROW_H + 6 : 34) + 34 + 10; + int cx = g_tb2_net_rect[0] + g_tb2_net_rect[2] - CW; + if (cx < 8) cx = 8; + if (cx + CW > W - 8) cx = W - 8 - CW; + int cy = (int)g_fb_h - TB_H - TB_BOTTOM_GAP - ch - 8; + + g_wifi_card_rect[0] = cx; g_wifi_card_rect[1] = cy; + g_wifi_card_rect[2] = CW; g_wifi_card_rect[3] = ch; + du_wf_card(&g_fb, cx, cy, CW, ch, 235, t); + + /* 标题行 */ + zh_draw_string(&g_fb, "WLAN", cx + 14, cy + 12, t->text_primary, 0); + /* 重新扫描按钮(右上) */ + int rx = cx + CW - 76, ry = cy + 8; + g_wifi_rescan_rect[0] = rx; g_wifi_rescan_rect[1] = ry; + g_wifi_rescan_rect[2] = 64; g_wifi_rescan_rect[3] = 32; + du_wf_button(&g_fb, rx, ry, 64, "Scan", t, tb_hover_state(g_wifi_rescan_rect), 0); + + int y = cy + 46; + + if (g_wifi_mode == 1) { + /* ---- 密码模式 ---- */ + zh_draw_string(&g_fb, g_wifi_sel_ssid, cx + 14, y, t->text_primary, 0); + y += 30; + g_wifi_pwbox_rect[0] = cx + 12; g_wifi_pwbox_rect[1] = y; + g_wifi_pwbox_rect[2] = CW - 24; g_wifi_pwbox_rect[3] = 36; + du_wf_textbox(&g_fb, cx + 12, y, CW - 24, g_wifi_pw, 1, t, 1); + y += 48; + int bw = (CW - 24 - 8) / 2; + g_wifi_cancel_rect[0] = cx + 12; g_wifi_cancel_rect[1] = y; + g_wifi_cancel_rect[2] = bw; g_wifi_cancel_rect[3] = 32; + du_wf_button(&g_fb, cx + 12, y, bw, "Cancel", t, + tb_hover_state(g_wifi_cancel_rect), 0); + g_wifi_ok_rect[0] = cx + 12 + bw + 8; g_wifi_ok_rect[1] = y; + g_wifi_ok_rect[2] = bw; g_wifi_ok_rect[3] = 32; + du_wf_button(&g_fb, cx + 12 + bw + 8, y, bw, "OK", t, + tb_hover_state(g_wifi_ok_rect), 1); + y += 42; + } else { + /* ---- 列表模式 ---- */ + if (g_wifi_dev < 0) { + zh_draw_string(&g_fb, g_wifi_status[0] ? g_wifi_status : "无无线设备", + cx + 14, y + 8, t->text_dim, 0); + y += 34; + } else if (g_wifi_scanning && g_wifi_n == 0) { + zh_draw_string(&g_fb, "正在扫描...", cx + 14, y + 8, t->text_dim, 0); + y += 34; + } else if (g_wifi_n == 0) { + zh_draw_string(&g_fb, "未发现网络", cx + 14, y + 8, t->text_dim, 0); + y += 34; + } else { + for (int i = 0; i < rows; i++) { + g_wifi_row_rect[i][0] = cx + 4; g_wifi_row_rect[i][1] = y; + g_wifi_row_rect[i][2] = CW - 8; g_wifi_row_rect[i][3] = WIFI_ROW_H; + int sel = (g_wifi_sel == i); + du_wf_list_item(&g_fb, cx + 4, y, CW - 8, + g_wifi_items[i].ssid, t, + tb_hover_state(g_wifi_row_rect[i]) == WF_BTN_HOVER, sel); + /* 右侧:加密标记 + 信号强度 */ + { + char tag[16]; + int p = 0; + if (g_wifi_items[i].security != 0u) tag[p++] = '*'; + int r = g_wifi_items[i].rssi; + int bars = (r >= -55) ? 3 : (r >= -70) ? 2 : 1; + tag[p++] = (char)('0' + bars); + tag[p] = 0; + du_draw_string(&g_fb, tag, cx + CW - 46, y + 10, + t->text_secondary, 0, DU_ASCII_STEP); + } + y += WIFI_ROW_H; + } + y += 6; + } + } + + /* 状态行 */ + if (g_wifi_status[0]) + zh_draw_string(&g_fb, g_wifi_status, cx + 14, y + 2, t->accent, 0); +} + +/* 卡片点击(taskbar_click 中调用;返回 1 = 已消费) */ +static int wifi_click(int mx, int my) { + if (!g_wifi_open) return 0; + if (!rect_hit(g_wifi_card_rect, mx, my)) { + g_wifi_open = 0; /* 点外部收起 */ + return 0; + } + /* 重新扫描 */ + if (rect_hit(g_wifi_rescan_rect, mx, my)) { + wifi_card_open(); + return 1; + } + if (g_wifi_mode == 1) { + if (rect_hit(g_wifi_ok_rect, mx, my)) { wifi_connect_go(); return 1; } + if (rect_hit(g_wifi_cancel_rect, mx, my)) { + g_wifi_mode = 0; + g_wifi_pw[0] = 0; + return 1; + } + return 1; /* 卡片内其他点击吞掉 */ + } + /* 列表行 */ + int rows = g_wifi_n; + if (rows > WIFI_MAX_ROWS) rows = WIFI_MAX_ROWS; + for (int i = 0; i < rows; i++) { + if (!rect_hit(g_wifi_row_rect[i], mx, my)) continue; + g_wifi_sel = i; + int p = 0; + for (; p < 32 && g_wifi_items[i].ssid[p]; p++) + g_wifi_sel_ssid[p] = g_wifi_items[i].ssid[p]; + g_wifi_sel_ssid[p] = 0; + g_wifi_sel_sec = g_wifi_items[i].security; + if (g_wifi_sel_sec == 0u) { + g_wifi_pw[0] = 0; + wifi_connect_go(); /* 开放网络直连 */ + } else { + g_wifi_mode = 1; /* 加密网络进密码输入 */ + g_wifi_pw[0] = 0; + g_wifi_status[0] = 0; + } + return 1; + } + return 1; +} + +/* 卡片键盘输入(shell.c handle_key 中调用;返回 1 = 已消费) */ +int wifi_key(u8 sc, int shift) { + if (!g_wifi_open) return 0; + if (sc == 0x01) { g_wifi_open = 0; return 1; } /* Esc 收起 */ + if (g_wifi_mode != 1) return 0; + if (sc == 0x0E) { /* Backspace */ + if (g_wifi_pw[0]) { + int l = 0; + while (g_wifi_pw[l]) l++; + g_wifi_pw[l - 1] = 0; + } + return 1; + } + if (sc == 0x1C) { wifi_connect_go(); return 1; } /* Enter 连接 */ + if (sc >= 58) return 0; + { + static const char normal[58] = { + 0, 27, '1','2','3','4','5','6','7','8','9','0','-','=', 8, '\t', + 'q','w','e','r','t','y','u','i','o','p','[',']','\n',0, + 'a','s','d','f','g','h','j','k','l',';','\'', '`',0,'\\', + 'z','x','c','v','b','n','m',',','.','/',0,'*',0,' ' + }; + static const char shifted[58] = { + 0, 27, '!','@','#','$','%','^','&','*','(',')','_','+', 8, '\t', + 'Q','W','E','R','T','Y','U','I','O','P','{','}','\n',0, + 'A','S','D','F','G','H','J','K','L',':','"','~',0,'|', + 'Z','X','C','V','B','N','M','<','>','?',0,'*',0,' ' + }; + char c = shift ? shifted[sc] : normal[sc]; + if (c && c != '\n' && c != 27 && c != 8) { + int l = 0; + while (g_wifi_pw[l]) l++; + if (l < WIFI_PW_MAX) { + g_wifi_pw[l] = c; + g_wifi_pw[l + 1] = 0; + } + } + } + return 1; +} /* ---- 开始菜单 logo(textures/startMenuLogo.rgba,加载失败回退字形) ---- */ static int g_smlogo_ok = 0; @@ -195,6 +532,9 @@ void taskbar_draw(void) { } g_tb2_tray_rect[0] = tx; g_tb2_tray_rect[1] = by; g_tb2_tray_rect[2] = tray_w; g_tb2_tray_rect[3] = TB_H; + + /* WiFi 快捷卡片(最顶) */ + wifi_draw(); } /* ============================================================ @@ -225,13 +565,18 @@ int taskbar_click(int mx, int my) { } return 1; } + /* WiFi 卡片(打开时优先消费;点外部收起并放行点击) */ + if (g_wifi_open) wifi_click(mx, my); + /* 托盘 */ if (rect_hit(g_tb2_pwr_rect, mx, my)) { g_quit = 1; /* M7:电源菜单(睡眠/重启/关机) */ return 1; } if (rect_hit(g_tb2_net_rect, mx, my)) { - slog("[taskbar] network (quick settings: M6)"); + /* 网络:弹出/收起 WiFi 快捷卡片(扫描 → 选网 → 密码 → 连接) */ + if (g_wifi_open) g_wifi_open = 0; + else wifi_card_open(); return 1; } if (rect_hit(g_tb2_vol_rect, mx, my)) { diff --git a/CODE/sdk/include/deshab/kernel_api.h b/CODE/sdk/include/deshab/kernel_api.h index 5a5cb97..36a2505 100644 --- a/CODE/sdk/include/deshab/kernel_api.h +++ b/CODE/sdk/include/deshab/kernel_api.h @@ -93,6 +93,11 @@ typedef struct dkm_net_api { int (*scan_count)(u32 index); int (*scan_result)(u32 index, u32 n, dkm_net_scan_result *out); int (*is_wireless)(u32 index); + /* 无线关联(dispatch 到 wlan 驱动回调;尾部追加,ABI 兼容。 + * 槽位布局必须与 UTSM include/utsm/net.h 镜像一致: + * connect @0x48, disconnect @0x50, device_stats @0x58)。 */ + int (*connect)(u32 index, const char *ssid, const char *password); + int (*disconnect)(u32 index); int (*device_stats)(u32 index, dkm_net_stats *out); } dkm_net_api;