launcher bd37c8b0→d10c0e8a; 改动文件 -Wall -Wextra 0 warning; 真机待验证: 蓝牙 pair/connect、开关联动、触摸几何 出入: 中文SSID/BSSID去重复用 net.c 既有实现; 模态复用 s_dd_modal(SDK 模态层未上收, 未改 SDK); s_net_open 旧分支留作死代码未删
216 lines
8.9 KiB
C
216 lines
8.9 KiB
C
/* 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);
|
||
}
|