Files
MeowOS-For-R3P2/patch_v2/meow_launcherd/wifi_page.c
T
AskaEth b20e3c43e9 fix(launcher): M47 设置页蓝牙/WiFi 开关只认真实态 — 三态查询 + 待确认收敛 + 未知态呈现
用户原话:「开关都存在显示问题 我都不能判断它是不是真的开启还是关闭(包括 WiFi 页也是)」。
根因不是「没读到硬件态」,而是「读到/没读到都被拿去当用户意图的装饰」:
- bt_page.c:103 开关初值来自 settings 布尔;:112-114 下发后立刻 g_sys.bt_on = on(显示意图);
  :129 c->bt_on = nm_bt_powered() 赋值后全文无人引用(M45 也独立发现)。
- wifi_page.c:40 读不到 settings 时默认 1(显示已开)。
- netmgrd 的 bt/wifi 查询本身把「问不到」塌缩成 0(nm_bt_powered/nm_wifi_connected)。

本单只解「状态可信」,不碰扫描/配对/音频(另有 M45/M46 单)。

1) 真实态三态(-1 未知 / 0 关 / 1 开)
- net.c 新增 net_bt_power_probe()(hciconfig hci0 UP/DOWN;无输出=未知)与
  net_wifi_power_probe()(rfkill wlan soft/hard 汇总,回退 wlan0 IFF_UP;读不到=未知)。
- netmgr_ipc.c 新增 nm_bt_power_state()(daemon bt status 的 available/powered 权威,不可达
  回退直连探测)与 nm_wifi_power_state()(netmgrd 的 wifi status 只有 wpa_state、不报射频态,
  故取本地 rfkill——daemon 的 wifi on/off 改的就是同一份内核状态)。
- 两个子页的 top_sw/rows 只读这两个函数;settings 布尔降级为「意图持久化」,不再决定外观。
- 「读不到默认 1」删除:未知就是未知。

2) 点击 → 待确认 → 真实态定稿
- 纯函数 meow_toggle_{display,pending,rollback,error}(bt_page.c,无 I/O/UI):
  只有 probe 确认才算收敛;probe 未知继续等,绝不因 last_real 恰好等于意图就宣布成功。
- 页面:落盘意图 → 下发 → 250ms 轮询(BT 上限 4s、WiFi 上限 5s;hciconfig 0.3~2s、
  wifi_up.sh 起 supplicant/DHCP 更慢,故不用 20ms 级忙轮询)→ 真实态定稿;失败/超时把
  settings 回滚到已确认真实态 + s_dd_modal 可见报错,不静默、不乐观。
- subpage.c:切换前先画「正在开启/关闭…(等待设备确认)」条(待确认态与稳定态可区分);
  切换后回读 top_sw 定稿(取消原「点了就翻面」),live tick 也回读,外部改动驻留期间可见。

3) 第三态呈现(未知 ≠ 已关)
- SDK meow_switch 只有两态:未知时开关停「关」并在左侧画灰色「?」;行内另有
  「蓝牙状态未知 / WiFi 开关状态未知」文字说明。选「?」是因为它不占用开关控件本身、
  且「关+?」不会被读成「已关」。

4) 顺带修 net.c 三条开关失真(塔追加的 M45 盘点,同一病根)
- s_net_toggle_bt() 补 rfkill soft(与 netmgrd 的 bt on/off 同一语义;开:先 unblock 再 up;
  关:先 down 再 block)。此前只发 hciconfig → 「BT 页关过再从头开」会被 rfkill 拦下。
- s_net_sync_from_sys() WiFi 判据从 wlan_connected()(已关联)改为射频态;BT 用三态,
  读不到不写(旧写法把「网卡 up 未关联」写成 0,把未知写成已关)。
- rfkill 同类多节点(本机 2 wlan + 2 bluetooth)按「任一 block 即 block」读、全部写入。

测试(`bash patch_v2/meow_launcherd/build_launcher.sh test`,新增 test 入口)
- test_bt_page.c:P4 三态与未知占位、P5 收敛四例(达成/下发失败/超时/未知)、P6 收敛矩阵
  12 组(真实开/关 × 下发成功/失败/超时 × probe 未知),全 PASS(64 条断言)。
- test_netmgr_ipc.c:新增 nm_bt_power_state 解析(powered=1/0、available=0、缺字段→-1)、
  nm_wifi_power_state 透传、不可达回退=未知、wifi_rows 未知态占位;并修掉两处既有测试缺陷
  (行内 UI 桩缺失导致本文件此前根本链不过;直连连接桩统一返回 "0\n" 导致等满 10s 必失败)。
- 交叉编译 0 warning(-Wall -Wextra,逐文件单独编 + 整链);launcherd 899388 B
  md5 1c88661a9410ac1a4139304362281cdd,同 commit 同步 scripts/build/fw_content.conf 钉值。

真机(10.58.215.50,仅只读自检,未替换 /usr/bin/meow_launcherd、未刷机)
- 二进制放 /usr/data/mnt/sd_0/MeowOS/tmp/ 跑 --sysdump:exit 0,输出 wifi_on=1 / bt_seen=1
  bt_on=0,与设备现状一致(rfkill 全 0 + wlan0 up + hci0 DOWN + netmgrd powered=0)。
- 卡上 du -s /usr/data/mnt/sd_0/Music 前后恒 199164544;临时文件已删;settings 未改。
- launcherd 在 squashfs 里 → 本改动需刷机生效。

已知不在本单:输出目标只写 settings(audiod 仅启动读一次)→ M45 finding / M46;
netmgrd 侧四处失真(未知当关、up 与 unblock 顺序、rfkill 只写首节点、wifi status 无射频字段)
已报 finding;设置主页/通知中心的开关显示仍是 settings(另立单)。
2026-11-11 17:16:28 +08:00

367 lines
17 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* wifi_page.c — MeowOS 系统设置「WiFi 独立子页面」(安卓风,M2.76;M6.0 迁移 netmgr IPC)。
*
* 页面(sub_page_t 脚手架 subpage.c 驱动):
* - 顶栏:返回 + 标题「WiFi」 + 右上开关(network.wifi_enabled)
* - 当前连接:SSID 行,点开 → 详情(SSID/本机IP/信号)+ [断开连接]
* - 已保存网络:每项点 → netmgr wifi connect(ssid,复用既有网络);当前连接的项标「已连接」
* - 附近网络:扫描结果(按 BSSID 去重;中文 SSID 经 wpa_decode_ssid 还原 \xNN → UTF-8)
* 点开 →【WiFi 新网络录入】加密网络弹 SDK IME 输密码(PW 模式)→ netmgr connect → 结果反馈;
* 开放网络直连。
*
* 【M6.0 迁移】全部查询/操作走 netmgr IPC(netmgr_ipc.c 的 nm_* 封装),netmgrd 是 WiFi 权威;
* netmgrd 不可达(NETMGR_UNREACH)→ 自动回退 net.c 直连 wpa_cli(双轨保功能),页面显示降级占位
* 「网络管理服务未运行(直连模式)」。
*
* 【M47 开关可信化】顶栏开关显示态只来自真实态三态查询 nm_wifi_power_state()(rfkill wlan
* soft/hard,回退 wlan0 IFF_UP;探测失败=未知),**读不到不再默认「已开」**;settings 的
* network.wifi_enabled 只承载用户意图。点击先出示「正在开启/关闭…(等待确认)」,下发 → 有界
* 轮询真实态 → 真实态定稿;失败/超时回滚并弹窗(不许乐观显示成功)。
*/
#include "meow_launcher_internal.h"
/* action code 判位(act 回调据 act 值定位网络) */
#define WF_ACT_CURR (-1) /* 当前连接(SUB_ROW,detail) */
#define WF_ACT_SAVED 0 /* 已保存网络 idx(SUB_ACT,connect) */
#define WF_ACT_SCAN_BASE 1000 /* 附近网络 1000+idx(SUB_ROW,连接) */
#define WF_ACT_MANUAL 2000 /* 「手动添加隐藏网络」动作行(SUB_ACT,connecth) */
typedef struct {
meow_net_t saved[S_NET_MAX];
meow_net_t scan[S_NET_MAX];
int saved_n, scan_n;
char ssid[64];
int connected; /* nm_wifi_connected()(IPC>直连回退 wlan_connected) */
int sw_state; /* 真实射频态三态:-1 未知 / 0 关 / 1 开(M47) */
unsigned long long sw_ts; /* 上面这次查询的时刻(缓存去重) */
long last_scan_ms; /* 扫描触发节流 */
} wifi_ctx_t;
/* M47:开关收敛参数(宿主测试可预先定义改小)。WiFi 走 wifi_up.sh(起 wpa_supplicant +
* 等关联/DHCP),比 BT 慢,所以上限 5s;250ms 探一次(≤20 次),不 20ms 级忙轮询。 */
#ifndef WF_TOGGLE_SETTLE_MS
#define WF_TOGGLE_SETTLE_MS 5000
#endif
#ifndef WF_TOGGLE_POLL_MS
#define WF_TOGGLE_POLL_MS 250
#endif
#define WF_STATE_TTL_MS 800
/* 真实态查询(带缓存)。定义见 netmgr_ipc.c:netmgrd 不报射频态,故取 rfkill/wlan0。 */
extern int nm_wifi_power_state(void);
/* 收敛判定纯函数,实现见 bt_page.c(两个页面共用一套;矩阵测试在 test_bt_page.c) */
extern int meow_toggle_display(int intent, int last_real, int dispatch_ok, int probe, int timed_out);
extern int meow_toggle_pending(int intent, int last_real, int dispatch_ok, int probe, int timed_out);
extern int meow_toggle_rollback(int intent, int last_real, int dispatch_ok, int probe, int timed_out);
extern int meow_toggle_error(int intent, int last_real, int dispatch_ok, int probe, int timed_out);
static int wifi_state_now(wifi_ctx_t *c, int force)
{
unsigned long long t = now_ms();
if (!force && c->sw_ts && t - c->sw_ts < WF_STATE_TTL_MS) return c->sw_state;
c->sw_state = nm_wifi_power_state();
c->sw_ts = t;
return c->sw_state;
}
/* 顶栏开关当前值:只报真实态(-1=未知 → subpage 画「?」)。绝不再「读不到就默认 1」。 */
static int wifi_top_sw(void *ctx, int *on)
{
wifi_ctx_t *c = ctx;
*on = wifi_state_now(c, 0);
return 1;
}
/* 顶栏开关切换:落盘意图 → 开/关网卡(netmgr on/off,回退直连)→ 有界等待真实态收敛 →
* 真实态定稿/回滚 + 可见错误提示。 */
static void wifi_top_sw_set(void *ctx, int on)
{
wifi_ctx_t *c = ctx;
int want = on ? 1 : 0;
int last_real = wifi_state_now(c, 0);
meow_settings_set_bool("network.wifi_enabled", want); /* 意图持久化 */
meow_settings_save();
int dispatch_ok = (nm_wifi_toggle(want) == MEOW_OK);
unsigned long long deadline = now_ms() + WF_TOGGLE_SETTLE_MS;
int probe = wifi_state_now(c, 1), timed_out = 0;
while (meow_toggle_pending(want, last_real, dispatch_ok, probe, timed_out)) {
if (now_ms() >= deadline) { timed_out = 1; break; }
usleep(WF_TOGGLE_POLL_MS * 1000);
probe = wifi_state_now(c, 1);
}
int disp = meow_toggle_display(want, last_real, dispatch_ok, probe, timed_out);
int rb = meow_toggle_rollback(want, last_real, dispatch_ok, probe, timed_out);
int err = meow_toggle_error(want, last_real, dispatch_ok, probe, timed_out);
c->sw_state = disp; c->sw_ts = now_ms();
meow_settings_set_bool("network.wifi_enabled", disp == 1); /* 只留已确认的真实态 */
meow_settings_save();
if (disp >= 0) g_sys.wifi_on = disp;
paint_sb_right(); /* 状态栏 WiFi 图标随「已确认」状态刷新 */
if (disp == 1) { nm_wifi_scan_trigger(); c->last_scan_ms = now_ms(); }
logmsg("wifi_page: toggle want=%d ok=%d probe=%d to=%d disp=%d rollback=%d err=%d",
want, dispatch_ok, probe, timed_out, disp, rb, err);
if (err) {
char line[96];
const char *txt[1]; txt[0] = line;
if (disp < 0) snprintf(line, sizeof line, "无法确认 WiFi 状态(射频/服务无响应)");
else snprintf(line, sizeof line, "切换未生效:当前仍为%s(%dms 内未确认)",
disp ? "开启" : "关闭", WF_TOGGLE_SETTLE_MS);
s_dd_modal("WiFi", txt, 1, -1);
}
}
/* 触发一次 WiFi 扫描(节流:1.5s 内不重复。IPC 路径 scan 子命令由 nm_wifi_scan 内含触发+读取) */
static void wifi_scan(void *ctx)
{
wifi_ctx_t *c = ctx;
if (!c->connected) return;
long t = (long)now_ms();
if (c->last_scan_ms && t - c->last_scan_ms < 1500) return;
c->last_scan_ms = t;
nm_wifi_scan_trigger();
}
/* 填行:刷新数据后构建分组列表(netmgr IPC 优先;不可达回退直连 + 降级占位) */
static int wifi_rows(void *ctx, sub_row_t *rbuf, int max)
{
wifi_ctx_t *c = ctx;
c->sw_state = wifi_state_now(c, 0); /* 真实射频态(三态);未知时下面显式说明 */
c->connected = nm_wifi_connected();
if (c->connected) {
nm_wifi_ssid(c->ssid, sizeof c->ssid);
wifi_scan(c);
} else {
c->ssid[0] = 0;
}
c->saved_n = nm_wifi_saved(c->saved, S_NET_MAX);
c->scan_n = nm_wifi_scan(c->scan, S_NET_MAX);
if (c->saved_n < 0) c->saved_n = 0;
if (c->scan_n < 0) c->scan_n = 0;
int n = 0;
/* 降级占位:netmgrd 不可达 → 提示(直连仍在跑,保功能) */
if (!netmgr_ok() && n < max) {
rbuf[n].kind = SUB_HDR; rbuf[n].label = "网络管理服务未运行(直连模式)";
rbuf[n].sub = NULL; rbuf[n].on = 0; rbuf[n].act = 0; n++;
}
/* 第三态显式呈现(M47):射频态未知 ≠ 已关;顶栏开关同时画「?」,这里补文字说明。 */
if (c->sw_state < 0 && n < max) {
rbuf[n].kind = SUB_ROW; rbuf[n].label = "WiFi 开关状态未知";
rbuf[n].sub = "无法读取射频状态(rfkill/网卡无响应)";
rbuf[n].on = 0; rbuf[n].act = -2; n++;
}
/* 当前连接 */
if (n < max) { rbuf[n].kind = SUB_HDR; rbuf[n].label = "当前连接"; rbuf[n].sub = NULL; rbuf[n].on = 0; rbuf[n].act = 0; n++; }
if (n < max) {
rbuf[n].kind = SUB_ROW;
rbuf[n].label = c->connected && c->ssid[0] ? "已连接" : "未连接";
rbuf[n].sub = c->connected && c->ssid[0] ? c->ssid : "";
rbuf[n].on = 0; rbuf[n].act = WF_ACT_CURR;
n++;
}
/* 已保存网络 */
if (n < max) { rbuf[n].kind = SUB_HDR; rbuf[n].label = "已保存网络"; rbuf[n].sub = NULL; rbuf[n].on = 0; rbuf[n].act = 0; n++; }
if (c->saved_n == 0) {
if (n < max) { rbuf[n].kind = SUB_ROW; rbuf[n].label = "无已保存网络"; rbuf[n].sub = ""; rbuf[n].on = 0; rbuf[n].act = -2; n++; }
} else for (int i = 0; i < c->saved_n && n < max; i++) {
rbuf[n].kind = SUB_ACT;
rbuf[n].label = c->saved[i].name[0] ? c->saved[i].name : "<未命名>";
rbuf[n].sub = (c->connected && c->ssid[0] && !strcmp(c->saved[i].name, c->ssid)) ? "已连接"
: (c->saved[i].mac[0] ? c->saved[i].mac : "");
rbuf[n].on = 0; rbuf[n].act = WF_ACT_SAVED + i;
n++;
}
/* 附近网络 */
if (n < max) { rbuf[n].kind = SUB_HDR; rbuf[n].label = "附近网络"; rbuf[n].sub = NULL; rbuf[n].on = 0; rbuf[n].act = 0; n++; }
if (c->scan_n == 0) {
if (n < max) { rbuf[n].kind = SUB_ROW; rbuf[n].label = "未扫描到附近网络"; rbuf[n].sub = ""; rbuf[n].on = 0; rbuf[n].act = -2; n++; }
} else for (int i = 0; i < c->scan_n && n < max; i++) {
rbuf[n].kind = SUB_ROW;
rbuf[n].label = c->scan[i].name[0] ? c->scan[i].name : "(隐藏网络)";
rbuf[n].sub = c->scan[i].sec[0] && strcmp(c->scan[i].sec, "Open") ? c->scan[i].sec
: (c->scan[i].mac[0] ? c->scan[i].mac : "");
rbuf[n].on = 0; rbuf[n].act = WF_ACT_SCAN_BASE + i;
n++;
}
/* bug2:附近网络末尾的「手动添加隐藏网络」入口(SUB_ACT → wifi_act)。 */
if (n < max) {
rbuf[n].kind = SUB_ACT;
rbuf[n].label = "手动添加隐藏网络";
rbuf[n].sub = "";
rbuf[n].on = 0;
rbuf[n].act = WF_ACT_MANUAL;
n++;
}
return n;
}
/* SDK IME 通用文本输入(MEOW_IME_ASCII:QWERTY,顶条「中」可切拼音输中文 SSID)。
* 返回 1=已输入;0=取消。 */
static int wifi_ask_text(char *out, int maxlen)
{
meow_ime_ctx ic;
memset(&ic, 0, sizeof ic);
ic.mode = MEOW_IME_ASCII;
ic.maxlen = maxlen;
ic.buf[0] = 0;
int res = meow_ui_text_input(&ic); /* 阻塞:提交后 ctx.buf 回填最终文本 */
if (res && ic.buf[0]) { snprintf(out, maxlen, "%s", ic.buf); return 1; }
return 0;
}
/* bug1:connect 阻塞前画非模态「正在连接…」横幅。子页单线程,阻塞期间画面停在此横幅。 */
static void wifi_connecting_banner(const char *ssid)
{
const meow_ui_theme_t *th = s_ui_theme();
int W = meow_ui_width();
int y = 108, h = 44;
char line[160];
snprintf(line, sizeof line, "正在连接 %s ...", ssid && ssid[0] ? ssid : "网络");
meow_ui_fill_rect(0, y, W, h, th->surface_container);
meow_ui_text_center(W / 2, y + h / 2 - meow_ui_text_height(20) / 2, line, 20, th->on_surface);
meow_dirty_full();
meow_ui_flush();
}
/* bug2:手动添加隐藏网络(附近网络占位行点击 / 「手动添加隐藏网络」动作行共用)。 */
static void wifi_manual_add(wifi_ctx_t *c)
{
char ssid[96] = "";
if (!wifi_ask_text(ssid, sizeof ssid)) return;
const char *items[2];
items[0] = "加密网络(WPA/WPA2/WPA3)";
items[1] = "开放网络";
int pick = s_dd_modal("网络加密方式", items, 2, -1);
if (pick < 0) return;
int open = (pick == 1);
char psk[96] = "";
if (!open && !wifi_ask_text(psk, sizeof psk)) return;
char err[160]; err[0] = 0;
wifi_connecting_banner(ssid);
int rc = nm_wifi_connect_ex(ssid, open ? NULL : psk, 1, err, sizeof err);
char line[192]; const char *txt[1]; txt[0] = line;
snprintf(line, sizeof line, "%s", err[0] ? err : (rc == MEOW_OK ? "已连接" : "连接失败"));
logmsg("wifi_page: manual connect ssid=%s hidden=1 open=%d rc=%d err=%s", ssid, open, rc, err);
s_dd_modal(ssid, txt, 1, -1);
nm_wifi_scan_trigger();
c->last_scan_ms = now_ms();
}
/* 附近网络点选 →【WiFi 新网络录入】连接流程。返回 1=重绘。 */
static int wifi_connect_scan(wifi_ctx_t *c, int scan_idx)
{
if (scan_idx < 0 || scan_idx >= c->scan_n) return 0;
meow_net_t *s = &c->scan[scan_idx];
if (!s->name[0] || !strcmp(s->name, "(隐藏网络)")) {
wifi_manual_add(c); /* bug2:占位隐藏网络 → 手动录入(SSID 待输) */
return 1;
}
int open = (s->sec[0] == 0 || !strcmp(s->sec, "Open"));
char psk[96] = "";
if (!open) {
if (!wifi_ask_text(psk, sizeof psk)) return 1; /* 用户取消输密码 */
}
char err[160]; err[0] = 0;
wifi_connecting_banner(s->name); /* bug1:阻塞前横幅,避免「点完无反馈」 */
int rc = nm_wifi_connect(s->name, open ? NULL : psk, err, sizeof err);
char line[192];
const char *txt[1]; txt[0] = line;
/* bug1:结果文案直接采用 netmgrd/直连回填的诚实文本(成功含 ip / 失败含原因)。 */
snprintf(line, sizeof line, "%s", err[0] ? err : (rc == MEOW_OK ? "已连接" : "连接失败"));
logmsg("wifi_page: scan connect ssid=%s open=%d rc=%d err=%s", s->name, open, rc, err);
/* 连接结果反馈(成功后进已保存网络列表由 rows() 重扫刷新) */
s_dd_modal(s->name, txt, 1, -1);
nm_wifi_scan_trigger(); /* 连上后重扫,已保存/当前连接尽量贴近最新 */
c->last_scan_ms = now_ms();
return 1;
}
/* 普通行(SUB_ROW)点击:当前连接详情(含断开)/ 附近网络连接 */
static int wifi_detail(void *ctx, int idx, int act)
{
(void)idx;
wifi_ctx_t *c = ctx;
if (act == WF_ACT_CURR) {
char info[S_NET_MAX][48];
const char *txt[S_NET_MAX];
int n = nm_wifi_info(info, S_NET_MAX);
if (n == 0) {
snprintf(info[0], sizeof info[0], "%s", c->connected ? "已连接(无详情)" : "未连接");
txt[0] = info[0]; n = 1;
} else {
for (int i = 0; i < n; i++) txt[i] = info[i];
}
if (c->connected && n < S_NET_MAX) { snprintf(info[n], sizeof info[n], "断开连接"); txt[n] = info[n]; n++; }
int pick = s_dd_modal(c->ssid[0] ? c->ssid : "当前连接", txt, n, -1);
if (pick >= 0 && c->connected && pick == n - 1) {
char err[96]; err[0] = 0;
int rc = nm_wifi_disconnect(err, sizeof err);
const char *rtxt[1]; char rline[128];
rtxt[0] = rline;
snprintf(rline, sizeof rline, "%s", rc == MEOW_OK ? "已断开" : (err[0] ? err : "断开失败"));
s_dd_modal("断开", rtxt, 1, -1);
c->last_scan_ms = now_ms();
}
return 1;
}
if (act >= WF_ACT_SCAN_BASE) return wifi_connect_scan(c, act - WF_ACT_SCAN_BASE);
return 0;
}
/* 动作行(SUB_ACT)点击:已保存网络 → netmgr connect(ssid;复用既有网络)。
* 另承接「手动添加隐藏网络」入口(bug2)。 */
static int wifi_act(void *ctx, int idx, int act)
{
(void)idx;
wifi_ctx_t *c = ctx;
if (act == WF_ACT_MANUAL) { wifi_manual_add(c); return 1; }
if (act >= WF_ACT_SCAN_BASE) return 0; /* 附近是 SUB_ROW(detail),不走这里 */
int sidx = act - WF_ACT_SAVED;
if (sidx < 0 || sidx >= c->saved_n) return 0;
if (c->saved[sidx].id < 0) return 0;
char err[160]; err[0] = 0;
wifi_connecting_banner(c->saved[sidx].name); /* bug1:阻塞前横幅 */
int rc = nm_wifi_connect(c->saved[sidx].name, NULL, err, sizeof err);
char line[192]; const char *txt[1]; txt[0] = line;
/* bug1:结果文案直接采用 netmgrd/直连回填的诚实文本(成功含 ip / 失败含原因)。 */
snprintf(line, sizeof line, "%s", err[0] ? err : (rc == MEOW_OK ? "已连接" : "连接失败"));
logmsg("wifi_page: saved connect ssid=%s rc=%d err=%s", c->saved[sidx].name, rc, err);
s_dd_modal(c->saved[sidx].name, txt, 1, -1);
nm_wifi_scan_trigger();
c->last_scan_ms = now_ms();
return 1; /* 重绘 + 重填行 */
}
/* 运行 WiFi 独立子页面(阻塞到返回) */
int run_wifi_page(void)
{
static wifi_ctx_t c; /* static:行次指针稳定 */
memset(&c, 0, sizeof c);
sub_page_t p;
memset(&p, 0, sizeof p);
p.title = "WiFi";
p.top_sw = wifi_top_sw;
p.top_sw_set = wifi_top_sw_set;
p.rows = wifi_rows;
p.act = wifi_act;
p.detail = wifi_detail;
p.live = 0; /* WiFi 列表在动作/开关时重扫即可,避免驻留 popen 轮询 */
p.ctx = &c;
/* 打开页面立即触发一次扫描,附近列表更贴近最新 */
nm_wifi_scan_trigger();
c.last_scan_ms = now_ms();
bt_role_refresh(0); /* 顺带刷新网络缓存(不影响本页) */
return meow_subpage_run(&p);
}