Files
MeowOS-For-R3P2/patch_v2/meow_launcherd/bt_page.c
T
MeowOS f2786552d5 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 旧分支留作死代码未删
2026-08-31 03:41:51 +08:00

216 lines
8.9 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.
/* bt_page.c — MeowOS 系统设置「蓝牙独立子页面」(安卓风,M2.76)。
*
* 页面(sub_page_t 脚手架 subpage.c 驱动):
* - 顶栏:返回 + 标题「蓝牙」 + 右上开关(network.bluetooth_enabled)
* - 已连接设备:tap → bluetoothctl disconnect(角色经 bt_role_refresh 判定)
* - 已配对设备:tap → bluetoothctl connect(已连者标「已连接」)
* - 附近设备:扫描发现(net_bt_scan,后台有界 bluetoothctl 写 /tmp 缓存,异步不阻塞)——
* 未配对者 tap → pair;已配对者 tap → connect。实机配对成功率/超时需真机验证。
*
* 与网络线共用 net.c(net_bt_connected/paired/scan/scan_trigger/hci_powered/bt_role)。
* 开关写 network.bluetooth_enabled + hciconfig;连接/断开/配对走 bluetoothctl(带超时)。
*/
#include "meow_launcher_internal.h"
#define BT_ACT_CONN_BASE 2000 /* 已连接 idx(tap→disconnect) */
#define BT_ACT_PAIRED_BASE 3000 /* 已配对 idx(tap→connect) */
#define BT_ACT_NEAR_BASE 4000 /* 附近 idx(tap→pair/connect) */
typedef struct {
meow_net_t conn[S_NET_MAX];
meow_net_t paired[S_NET_MAX];
meow_net_t near[S_NET_MAX];
int conn_n, paired_n, near_n;
int bt_on;
int full; /* 1=本次 rows() 全量重扫(conn/paired popen);0=仅附近(file)+角色(节流) */
char mode[48]; /* bt_role 中文描述(次行摘要) */
} bt_ctx_t;
/* 设备 MAC(net_bt_* 已填 mac) */
static int bt_is_conn(const bt_ctx_t *c, const char *mac)
{
if (!mac[0]) return 0;
for (int i = 0; i < c->conn_n; i++) if (!strcmp(c->conn[i].mac, mac)) return 1;
return 0;
}
static int bt_is_paired(const bt_ctx_t *c, const char *mac)
{
if (!mac[0]) return 0;
for (int i = 0; i < c->paired_n; i++) if (!strcmp(c->paired[i].mac, mac)) return 1;
return 0;
}
/* 顶栏开关当前值 */
static int bt_top_sw(void *ctx, int *on)
{
(void)ctx;
int v = 0;
if (meow_settings_get_bool("network.bluetooth_enabled", &v) != 0) v = 0;
*on = v;
return 1;
}
/* 顶栏开关切换:写设置 + hciconfig up/down + 触发扫描 + 同步状态栏 */
static void bt_top_sw_set(void *ctx, int on)
{
(void)ctx;
meow_settings_set_bool("network.bluetooth_enabled", on);
s_net_toggle_bt(on);
g_sys.bt_on = on;
bt_role_refresh(1);
paint_sb_right();
meow_settings_save();
if (on) net_bt_scan_trigger();
}
/* 填行:刷新数据后构建分组列表。
* conn/paired 走 bluetoothctl(慢);仅全量(full)时重扫,避开子页 live tick 反复 spawn。 */
static int bt_rows(void *ctx, sub_row_t *rbuf, int max)
{
bt_ctx_t *c = ctx;
int full = c->full; c->full = 0; /* 消费一次全量标记 */
c->bt_on = hci_powered();
if (full || c->conn_n < 0) { /* 首次/动作后:全量重扫 conn/paired */
c->conn_n = net_bt_connected(c->conn, S_NET_MAX);
c->paired_n = net_bt_paired(c->paired, S_NET_MAX);
if (c->conn_n < 0) c->conn_n = 0;
if (c->paired_n < 0) c->paired_n = 0;
}
c->near_n = net_bt_scan(c->near, S_NET_MAX); /* 附近:读 /tmp 缓存(快,实测无 popen) */
if (c->near_n < 0) c->near_n = 0;
/* 角色描述:节流(≤5s)重探,读缓存,避免子页驻留期间反复 popen */
net_bt_mode_str(c->mode, sizeof c->mode);
int n = 0;
/* 已连接设备 */
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->conn_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->conn_n && n < max; i++) {
rbuf[n].kind = SUB_ACT;
rbuf[n].label = c->conn[i].name[0] ? c->conn[i].name : c->conn[i].mac;
rbuf[n].sub = c->conn[i].mac;
rbuf[n].on = 1; rbuf[n].act = BT_ACT_CONN_BASE + 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->paired_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->paired_n && n < max; i++) {
rbuf[n].kind = SUB_ACT;
rbuf[n].label = c->paired[i].name[0] ? c->paired[i].name : c->paired[i].mac;
rbuf[n].sub = bt_is_conn(c, c->paired[i].mac) ? "已连接"
: (c->paired[i].mac[0] ? c->paired[i].mac : "");
rbuf[n].on = bt_is_conn(c, c->paired[i].mac);
rbuf[n].act = BT_ACT_PAIRED_BASE + 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->near_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->near_n && n < max; i++) {
int is_paired = bt_is_paired(c, c->near[i].mac);
if (is_paired) continue; /* 已配对者归于「已配对设备」,附近只列未配对 */
rbuf[n].kind = SUB_ACT;
rbuf[n].label = c->near[i].name[0] ? c->near[i].name : c->near[i].mac;
rbuf[n].sub = c->near[i].mac;
rbuf[n].on = 0; rbuf[n].act = BT_ACT_NEAR_BASE + i;
n++;
}
return n;
}
/* 执行 bluetoothctl 命令(带超时)+ 结果弹窗反馈 */
static void bt_run(const char *title, const char *cmd)
{
char *o = popen_read_tmo(8000, cmd);
char line[48];
const char *txt[4]; txt[0] = line;
int ok = o && (strstr(o, "successful") || strstr(o, "Connected") ||
strstr(o, "already") || strstr(o, "Paired"));
snprintf(line, sizeof line, "%s", ok ? "操作成功" : "操作失败/超时");
if (o) free(o);
s_dd_modal(title, txt, 1, -1);
}
/* 动作行(SUB_ACT)点击:连接/断开/配对 */
static int bt_act(void *ctx, int idx, int act)
{
(void)idx;
bt_ctx_t *c = ctx;
char mac[24] = "";
/* 定位 MAC(按 act 段) */
if (act >= BT_ACT_NEAR_BASE) {
int i = act - BT_ACT_NEAR_BASE;
if (i < 0 || i >= c->near_n) return 0;
snprintf(mac, sizeof mac, "%s", c->near[i].mac);
} else if (act >= BT_ACT_PAIRED_BASE) {
int i = act - BT_ACT_PAIRED_BASE;
if (i < 0 || i >= c->paired_n) return 0;
snprintf(mac, sizeof mac, "%s", c->paired[i].mac);
} else if (act >= BT_ACT_CONN_BASE) {
int i = act - BT_ACT_CONN_BASE;
if (i < 0 || i >= c->conn_n) return 0;
snprintf(mac, sizeof mac, "%s", c->conn[i].mac);
}
if (!mac[0]) return 0;
if (act >= BT_ACT_NEAR_BASE) {
/* 附近未配对 → pair;已配对(理论上过滤了)→ connect */
char cmd[192];
snprintf(cmd, sizeof cmd, "PATH=/bin:/sbin:/usr/bin:/usr/sbin bluetoothctl pair %s 2>/dev/null", mac);
bt_run("配对", cmd);
} else if (act >= BT_ACT_PAIRED_BASE) {
/* 已配对:未连 → connect;已连 → disconnect */
if (bt_is_conn(c, mac)) {
char cmd[192];
snprintf(cmd, sizeof cmd, "PATH=/bin:/sbin:/usr/bin:/usr/sbin bluetoothctl disconnect %s 2>/dev/null", mac);
bt_run("断开", cmd);
} else {
char cmd[192];
snprintf(cmd, sizeof cmd, "PATH=/bin:/sbin:/usr/bin:/usr/sbin bluetoothctl connect %s 2>/dev/null", mac);
bt_run("连接", cmd);
}
} else {
/* 已连接 → disconnect */
char cmd[192];
snprintf(cmd, sizeof cmd, "PATH=/bin:/sbin:/usr/bin:/usr/sbin bluetoothctl disconnect %s 2>/dev/null", mac);
bt_run("断开", cmd);
}
bt_role_refresh(1);
net_bt_scan_trigger();
c->full = 1; /* 动作后:下一次 rows() 全量重扫 */
logmsg("bt_page: act act=%d mac=%s", act, mac);
return 1; /* 重绘 + 重填行 */
}
/* 运行蓝牙独立子页面(阻塞到返回) */
int run_bt_page(void)
{
static bt_ctx_t c;
memset(&c, 0, sizeof c);
sub_page_t p;
memset(&p, 0, sizeof p);
p.title = "蓝牙";
p.top_sw = bt_top_sw;
p.top_sw_set = bt_top_sw_set;
p.rows = bt_rows;
p.act = bt_act;
p.detail = NULL; /* 蓝牙动作都走 SUB_ACT(act),无 SUB_ROW 详情 */
p.live = 1; /* 附近扫描驻留更新(约 2s 重扫行;仅附近/角色,不反复 spawn) */
p.ctx = &c;
c.full = 1; /* 首行全量重扫 */
net_bt_scan_trigger(); /* 进页立即触发一次附近扫描(异步,不阻塞) */
bt_role_refresh(1);
return meow_subpage_run(&p);
}