feat(launcher): 蓝牙/WiFi 独立子页面(安卓风)— 新增 subpage.c 统一脚手架(顶栏+分组列表+跟手滚动+左缘返回) + wifi_page.c(开关/当前连接详情/已保存/附近,BSSID去重+中文SSID) + bt_page.c(开关/已连接/已配对/附近扫描配对);SMENU 网络组收敛为 2 入口行 (agent-178)

launcher bd37c8b0→d10c0e8a; 改动文件 -Wall -Wextra 0 warning; 真机待验证: 蓝牙 pair/connect、开关联动、触摸几何
出入: 中文SSID/BSSID去重复用 net.c 既有实现; 模态复用 s_dd_modal(SDK 模态层未上收, 未改 SDK); s_net_open 旧分支留作死代码未删
This commit is contained in:
MeowOS committed 2026-08-31 03:41:51 +08:00
1 parent 366bf6bb5d
commit f2786552d5
10 files changed
+1122 -86

No files matched your search

+75 -14
View File
@@ -59,7 +59,7 @@ int proc_by_comm(const char *target)
static char g_bt_mode[48] = "开启未连接"; /* 设置页"蓝牙模式"行展示(bt_role_refresh 刷新) */
static int bt_role(char *desc, int len); /* 前向声明;实现在网络段(用 popen 带超时) */
int bt_role(char *desc, int len); /* 前向声明;实现在网络段(用 popen 带超时),跨模块给 bt_page 用 */
/* ===== M2.39/M2.40 网络/WiFi/蓝牙(设置页「网络」分组)=====
@@ -186,7 +186,7 @@ void net_wifi_ssid(char *buf, int len)
* 填充 info[][48](调用方提供的二维缓冲),每行 "标签: 值",返回行数(≤max)。
* 当前SSID / AP MAC(bssid) / 本机MAC(address) / AP IP(默认网关) / 本机IP(ip_address) / 频道freq。
* 未连接返回 0(调用方显示"未连接")。绝不阻塞:popen 带超时。 */
static int net_wifi_info(char (*info)[48], int max)
int net_wifi_info(char (*info)[48], int max)
{
if (!wlan_connected()) return 0;
char *o = popen_read_tmo(1500,
@@ -217,8 +217,8 @@ static int net_wifi_info(char (*info)[48], int max)
/* 已保存网络(wpa_cli -i wlan0 list_networks):首行为表头,其余每行 "<id>\t<ssid>\t<bssid>\t<flag>"。
* 填 out[i].id/.name(SSID,中文经 wpa_decode_ssid 还原 \xNN),返回项数(≤max)。 */
static int net_wifi_saved(meow_net_t *out, int max)
* 填 out[i].id/.name(SSID,中文经 wpa_decode_ssid 还原 \xNN)/.mac(BSSID),返回项数(≤max)。 */
int net_wifi_saved(meow_net_t *out, int max)
{
int n = 0;
char *o = popen_read_tmo(1500, "PATH=/bin:/sbin:/usr/bin:/usr/sbin wpa_cli -i wlan0 list_networks 2>/dev/null");
@@ -234,6 +234,9 @@ static int net_wifi_saved(meow_net_t *out, int max)
char *sp = p; while (*sp && *sp != ' ' && *sp != '\t') sp++;
while (*sp == ' ' || *sp == '\t') sp++;
char *se = sp; while (*se && *se != ' ' && *se != '\t' && *se != '\n' && *se != '\r') se++;
/* BSSID 列(SSID 后):<id>\t<ssid>\t<bssid>\t<flag>,拷进 mac 供详情/连接展示 */
char *bp = se; while (*bp == ' ' || *bp == '\t') bp++;
char *be = bp; while (*be && *be != ' ' && *be != '\t' && *be != '\n' && *be != '\r') be++;
if (sp != se) {
/* M2.40:list_networks 对中文 SSID 输出 \xNN 转义文字(如 \xe5\xa4\xa9… = "天…"),
* 先拷进临时缓冲再还原成真实 UTF-8 字节存入 name,否则界面显示超长乱码。 */
@@ -243,6 +246,10 @@ static int net_wifi_saved(meow_net_t *out, int max)
memcpy(raw, sp, (size_t)l); raw[l] = 0;
out[n].id = id;
wpa_decode_ssid(raw, out[n].name, sizeof out[n].name);
if (be > bp && (be - bp) < (int)sizeof out[n].mac) {
memcpy(out[n].mac, bp, (size_t)(be - bp));
out[n].mac[be - bp] = 0;
}
n++;
}
}
@@ -255,7 +262,7 @@ static int net_wifi_saved(meow_net_t *out, int max)
/* WiFi 扫描触发(异步:wpa_cli scan 只是通知 supplicant 发起扫描,立即返回)。
* 结果稍后经 net_wifi_scan() 读。 */
static void net_wifi_scan_trigger(void)
void net_wifi_scan_trigger(void)
{
if (!wlan_connected()) return;
system("PATH=/bin:/sbin:/usr/bin:/usr/sbin wpa_cli -i wlan0 scan 2>/dev/null");
@@ -267,8 +274,8 @@ static void net_wifi_scan_trigger(void)
* flags 恒以 '[' 起;SSID 为 flags 后的整段余量(可含空格;为空=隐藏网络)。
* M2.40:同一 AP 常出现多条(不同频段/空 SSID/重名),按 BSSID 去重——每个 BSSID 只留一条,
* 优先采用含完整 SSID 的行(后到且有 SSID 则覆盖先到的空 SSID 行),隐藏网络显示 "(隐藏网络)"。
* 填 out[i].id=-1;name 存 SSID(或隐藏标记)。返回项数(≤max,且 ≤S_NET_MAX)。 */
static int net_wifi_scan(meow_net_t *out, int max)
* 填 out[i].id=-1;name 存 SSID(或隐藏标记)/mac 存 BSSID。返回项数(≤max,且 ≤S_NET_MAX)。 */
int net_wifi_scan(meow_net_t *out, int max)
{
int n = 0;
if (max > S_NET_MAX) max = S_NET_MAX;
@@ -315,6 +322,7 @@ static int net_wifi_scan(meow_net_t *out, int max)
seen_bssid[idx][sizeof seen_bssid[idx] - 1] = 0;
named[idx] = has;
out[idx].id = -1;
snprintf(out[idx].mac, sizeof out[idx].mac, "%s", bssid); /* 保留 BSSID 供详情展示 */
if (has) {
/* M2.41:扫描结果 SSID 也可能是 wpa_cli 的 \xNN 转义(中文),还原成真 UTF-8 */
char raw[96];
@@ -341,8 +349,8 @@ static int net_wifi_scan(meow_net_t *out, int max)
/* 蓝牙已配对设备(bluetoothctl paired-devices):每行 "Device <MAC> <Name>"。
* hci 未上电则返回 0(避免 spawn bluetoothctl 白跑)。 */
static int net_bt_paired(meow_net_t *out, int max)
* 填 out[i].mac=MAC / .name=名称;hci 未上电则返回 0(避免 spawn bluetoothctl 白跑)。 */
int net_bt_paired(meow_net_t *out, int max)
{
if (!hci_powered()) return 0;
int n = 0;
@@ -358,11 +366,15 @@ static int net_bt_paired(meow_net_t *out, int max)
char *mac = p; while (*mac && *mac != ' ' && *mac != '\t') mac++;
char *nm = mac; while (*nm == ' ' || *nm == '\t') nm++;
/* 设备名每行即名称 token;名称可含空格,这里取整行剩余部分(含 MAC 后的名字) */
int ml = (int)(mac - p); if (ml > 47) ml = 47;
if (ml > 0 && ml < (int)sizeof out[n].mac) {
memcpy(out[n].mac, p, (size_t)ml); out[n].mac[ml] = 0;
}
if (*nm)
snprintf(out[n].name, sizeof out[n].name, "%s", nm);
else {
int ml = (int)(mac - p); if (ml > 47) ml = 47;
memcpy(out[n].name, p, (size_t)ml); out[n].name[ml] = 0;
int l = ml; if (l > 47) l = 47;
memcpy(out[n].name, p, (size_t)l); out[n].name[l] = 0;
}
out[n].id = -1;
n++;
@@ -374,6 +386,42 @@ static int net_bt_paired(meow_net_t *out, int max)
}
/* 蓝牙已连接设备(bluetoothctl `devices Connected`):每行 "Device <MAC> <Name>"。
* M2.76:供蓝牙独立子页面「已连接设备」分组展示。hci 未上电返回 0。 */
int net_bt_connected(meow_net_t *out, int max)
{
if (!hci_powered()) return 0;
char *o = popen_read_tmo(1500,
"PATH=/bin:/sbin:/usr/bin:/usr/sbin printf 'devices Connected\\nquit\\n' | bluetoothctl 2>/dev/null");
if (!o) return 0;
int n = 0;
char *line = o;
while (line && *line && n < max) {
char *eol = strchr(line, '\n');
if (eol) *eol = 0;
char *q = line; while (*q == ' ' || *q == '\t') q++;
if (!strncmp(q, "Device ", 7)) {
q += 7;
char *mac = q; while (*mac && *mac != ' ' && *mac != '\t' && *mac != '\n' && *mac != '\r') mac++;
char *nm = mac; while (*nm == ' ' || *nm == '\t') nm++;
int ml = (int)(mac - q); if (ml > 47) ml = 47;
if (ml > 0 && ml < (int)sizeof out[n].mac) {
memcpy(out[n].mac, q, (size_t)ml); out[n].mac[ml] = 0;
}
char *e = nm; while (*e && *e != '\n' && *e != '\r') e++;
*e = 0;
if (*nm) snprintf(out[n].name, sizeof out[n].name, "%s", nm);
else { int l = ml; if (l > 47) l = 47; memcpy(out[n].name, q, (size_t)l); out[n].name[l] = 0; }
out[n].id = -1;
n++;
}
line = eol ? eol + 1 : NULL;
}
free(o);
return n;
}
/* ---- 蓝牙附近设备(未配对扫描发现;异步,不阻塞主循环)----
* M2.40:用户要的「附近的设备」= 蓝牙扫描发现(含未配对),而不只是已配对。扫描较慢(2-6s),
* 故后台有界驱动 bluetoothctl:scan on → sleep 4 → scan off → devices → quit,输出写 /tmp 缓存;
@@ -382,7 +430,7 @@ static int net_bt_paired(meow_net_t *out, int max)
static long g_bt_scan_ms = 0; /* 最近一次发起扫描的 now_ms();0=从未 */
static void net_bt_scan_trigger(void)
void net_bt_scan_trigger(void)
{
if (!hci_powered()) return;
long t = (long)now_ms();
@@ -395,7 +443,7 @@ static void net_bt_scan_trigger(void)
/* 读蓝牙附近设备缓存:解析 bluetoothctl `devices` 输出("Device <MAC> <Name>",去重;含已配对
* 与未配对但已发现的设备)。缓存不存在/为空则退回已配对列表(保证至少展示已知设备)。
* 返回项数(≤max,且 ≤S_NET_MAX)。 */
static int net_bt_scan(meow_net_t *out, int max)
int net_bt_scan(meow_net_t *out, int max)
{
if (!hci_powered()) return 0;
if (max > S_NET_MAX) max = S_NET_MAX;
@@ -424,6 +472,9 @@ static int net_bt_scan(meow_net_t *out, int max)
char *e = nm; while (*e && *e != '\n' && *e != '\r') e++;
*e = 0; /* 去行尾换行 */
strncpy(seen[n], dv, (size_t)ml); seen[n][ml] = 0;
if (ml < (int)sizeof out[n].mac) {
memcpy(out[n].mac, dv, (size_t)ml); out[n].mac[ml] = 0; /* 保留 MAC 供配对/连接 */
}
if (*nm)
snprintf(out[n].name, sizeof out[n].name, "%s", nm);
else {
@@ -484,7 +535,7 @@ static int a2dp_side(const char *sec)
/* 蓝牙角色:见上方注释。一次 popen 拿 ACL + A2DP + 已连设备名三类信息,避免反复 spawn。 */
static int bt_role(char *desc, int len)
int bt_role(char *desc, int len)
{
if (desc && len > 0) desc[0] = 0;
if (!g_sys.bt_seen || !hci_powered()) {
@@ -575,6 +626,14 @@ void bt_role_refresh(int force)
g_sys.bt_on = (g_sys.bt_role != BT_ROLE_OFF) ? 1 : 0;
}
/* M2.76:蓝牙角色中文描述(节流刷新,读缓存 g_bt_mode;供蓝牙子页摘要行,
* 避免子页驻留期间每 tick 反复 popen bt_role)。 */
void net_bt_mode_str(char *buf, int len)
{
bt_role_refresh(0);
snprintf(buf, len, "%s", g_bt_mode);
}
/* 网络信息缓存(绘制路径只读,s_net_refresh 刷新;下拉打开时才重新解析列表) */
static char g_net_ssid[64] = "未连接";
@@ -616,6 +675,8 @@ int s_net_get(const srow_t *r, char *buf, int len)
case NET_LIST_BT_NEARBY: snprintf(buf, len, "%d 台设备", g_net_bt_n); return 0;
case NET_LIST_BT_MODE: snprintf(buf, len, "%s", g_bt_mode); return 0;
case NET_LIST_ABOUT: snprintf(buf, len, "MeowOS v%s", g_meowos_version); return 0;
case NET_LIST_WIFI_PAGE: snprintf(buf, len, "%s", g_net_ssid); return 0; /* M2.76:子页入口摘要 */
case NET_LIST_BT_PAGE: snprintf(buf, len, "%s", g_bt_mode); return 0; /* M2.76 */
}
snprintf(buf, len, "-");
return 0;