Files
MeowOS-For-R3P2/patch_v2/meow_launcherd/wifi_page.c
T
MeowOS 3b6a73ac10 feat(launcher, NETMGR_DESIGN §12): 设置页 WiFi/蓝牙子页迁 netmgr IPC + WiFi 新网络录入 UI
- wifi_page/bt_page 查询/操作改走 netmgrd IPC(netmgr_ipc.c 客户端:
  socket /usr/data/meow_net.sock,HELLO 鉴权 → WIFI_CTL/BT_CTL → RESULT)。
- WiFi 新网络录入:扫描结果点选 →(加密网络)SDK IME 输密码 → connect →
  结果反馈 → 刷新已存网络;开放网络直连;当前连接详情含断开。
- 双轨过渡:netmgrd 不可达(NETMGR_UNREACH)回退 net.c 直连 wpa_cli/hciconfig
  (保功能),页面显示「网络管理服务未运行(直连模式)」降级占位(不崩)。
- BT 仅迁 on/off/status/paired(netmgrd BT_CTL 无连接/配对子命令,其余直连)。
- net.c net_wifi_scan 补 sec 字段(wifi_page.c 补 meow_net_t.sec)。
- 测试:+test_netmgr_ipc.c(假 socket 帧:扫描渲染/连接成功失败两态/降级占位)。
2026-09-01 22:09:31 +08:00

254 lines
11 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(双轨保功能),页面显示降级占位
* 「网络管理服务未运行(直连模式)」。
*/
#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,连接) */
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 wifi_on; /* network.wifi_enabled */
long last_scan_ms; /* 扫描触发节流 */
} wifi_ctx_t;
/* 顶栏开关当前值 */
static int wifi_top_sw(void *ctx, int *on)
{
(void)ctx;
int v = 1;
if (meow_settings_get_bool("network.wifi_enabled", &v) != 0) v = 1;
*on = v;
return 1;
}
/* 顶栏开关切换:写设置 + 开/关网卡(netmgr on/off,回退直连)+ 触发重扫 + 同步状态栏 */
static void wifi_top_sw_set(void *ctx, int on)
{
wifi_ctx_t *c = ctx;
meow_settings_set_bool("network.wifi_enabled", on);
nm_wifi_toggle(on);
g_sys.wifi_on = on;
paint_sb_right(); /* 状态栏 WiFi 图标随开关即时刷新 */
meow_settings_save();
if (on) { nm_wifi_scan_trigger(); c->last_scan_ms = now_ms(); }
}
/* 触发一次 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->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++;
}
/* 当前连接 */
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++;
}
return n;
}
/* SDK IME 输密码(PW 模式=ASCII 软键盘输入,现有输入法复用;回车提交/ESC或点背景取消)。
* 返回 1=已输入;0=取消。 */
static int wifi_ask_psk(char *psk, 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(psk, maxlen, "%s", ic.buf); return 1; }
return 0;
}
/* 附近网络点选 →【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, "(隐藏网络)")) {
const char *txt[1]; txt[0] = "隐藏网络无法直接连接";
s_dd_modal("附近网络", txt, 1, -1);
return 1;
}
int open = (s->sec[0] == 0 || !strcmp(s->sec, "Open"));
char psk[96] = "";
if (!open) {
if (!wifi_ask_psk(psk, sizeof psk)) return 1; /* 用户取消输密码 */
}
char err[160]; err[0] = 0;
int rc = nm_wifi_connect(s->name, open ? NULL : psk, err, sizeof err);
char line[192];
const char *txt[1]; txt[0] = line;
if (rc == MEOW_OK) {
snprintf(line, sizeof line, "连接成功:%s%s", s->name,
err[0] ? "" : "(已启用)");
} else {
snprintf(line, sizeof line, "连接失败:%s", err[0] ? err : "netmgr 返回错误");
}
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;复用既有网络) */
static int wifi_act(void *ctx, int idx, int act)
{
(void)idx;
wifi_ctx_t *c = ctx;
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;
int rc = nm_wifi_connect(c->saved[sidx].name, NULL, err, sizeof err);
char line[192]; const char *txt[1]; txt[0] = line;
if (rc == MEOW_OK) snprintf(line, sizeof line, "已连接:%s", c->saved[sidx].name);
else snprintf(line, sizeof line, "连接失败:%s", err[0] ? err : "netmgr 返回错误");
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);
}