launcher bd37c8b0→d10c0e8a; 改动文件 -Wall -Wextra 0 warning; 真机待验证: 蓝牙 pair/connect、开关联动、触摸几何 出入: 中文SSID/BSSID去重复用 net.c 既有实现; 模态复用 s_dd_modal(SDK 模态层未上收, 未改 SDK); s_net_open 旧分支留作死代码未删
505 lines
26 KiB
C
505 lines
26 KiB
C
/* subpage.c — MeowOS launcher 独立子页面(WiFi/蓝牙)统一脚手架(M2.76)。
|
||
*
|
||
* 安卓风子页骨架:顶栏(标题 + 返回 + 右上开关)+ 分组列表(组标题/行/开关/动作)
|
||
* + 滚动(跟手,方向沿用设置页 input.swipe_invert 语义)+ 左缘左滑返回 + 底部返回。
|
||
*
|
||
* 由页面(wifi_page.c / bt_page.c)通过 sub_page_t 提供:
|
||
* - title 顶栏标题
|
||
* - top_sw 顶栏开关(0=不显示;1=显示并回写当前值)
|
||
* - top_sw_set 开关切换(写设置 + 生效 + 触发重扫)
|
||
* - rows 填行(组标题/普通行/开关行/动作行),返回行数
|
||
* - act 点动作/开关行
|
||
* - detail 点普通行(如当前连接详情、网络/设备详情)
|
||
* - live 1=驻留时周期性(约2s)重扫行(蓝牙附近扫描等动态数据)
|
||
*
|
||
* 与设置页同主题(s_ui_theme,MD3 语义色,明暗自动);局部刷新不整屏重绘;
|
||
* 模态弹窗交给 launcher 既有 s_dd_modal(点外关闭不穿透),本脚手架在回调返回后
|
||
* 彻底排空本页 tfd + 重置触摸基线(复刻 run_settings_page 后置排空,防穿透)。
|
||
*
|
||
* 物理键:电源=系统级语义(单击息屏/亮屏、长按电源菜单、8s 强关)、音量=系统音量、
|
||
* 播放=抓住——不再映射为返回(APPS §3.4.1 全局接管)。
|
||
*/
|
||
#include "meow_launcher_internal.h"
|
||
|
||
|
||
/* ---- 布局常量(顶栏/内容区;全屏内容区延用设置页 surface_variant 整屏底)---- */
|
||
#define SUB_TB_Y SB_BAR_H /* 28:顶栏顶缘(状态栏下) */
|
||
#define SUB_TB_H 68 /* 顶栏高 28..96 */
|
||
#define SUB_TB_BOT (SUB_TB_Y + SUB_TB_H) /* 96:顶栏底缘 / 分隔线 */
|
||
#define SUB_CONT_TOP (SUB_TB_BOT + 6) /* 102:内容区顶缘 */
|
||
#define SUB_ROW_H 58 /* 行高(对齐 SROW_H,便于触摸) */
|
||
#define SUB_HDR_H 40 /* 组标题行高 */
|
||
#define SUB_PAD_X 16 /* 水平内边距 */
|
||
#define SUB_SW_W 48 /* 开关宽(s_draw_switch) */
|
||
#define SUB_SW_H 28
|
||
#define SUB_BTN_X 8 /* 底部返回按钮左缘 */
|
||
#define SUB_BTN_W 120 /* 底部返回按钮宽 */
|
||
|
||
|
||
/* 返回行高:组标题 40,其余 58 */
|
||
static int sub_row_h(const sub_row_t *r)
|
||
{
|
||
return (r->kind == SUB_HDR) ? SUB_HDR_H : SUB_ROW_H;
|
||
}
|
||
|
||
/* 滚动 clamp 到 [0, max](scroll_max 由调用方保证 >=0) */
|
||
static int sub_clamp(int v, int max)
|
||
{
|
||
if (v < 0) v = 0;
|
||
if (v > max) v = max;
|
||
return v;
|
||
}
|
||
|
||
|
||
/* ---- 计算行 y 前缀和(ypos[i]=第 i 行顶缘相对内容区顶;ypos[n]=总高)---- */
|
||
static void sub_ypos(const sub_row_t *rows, int n, int ypos[SUB_ROWS_MAX + 1])
|
||
{
|
||
ypos[0] = 0;
|
||
for (int i = 0; i < n; i++) ypos[i + 1] = ypos[i] + sub_row_h(&rows[i]);
|
||
}
|
||
|
||
|
||
/* ---- 绘制子页(进 back_buf;调用方再局部标脏 + flush)----
|
||
* rows/nrows 为当前行集;scroll_off 为滚动偏移;top_on 为顶栏开关当前值;
|
||
* top_sw_en 为是否显示顶栏开关。 */
|
||
static void subpage_paint(sub_page_t *p, const sub_row_t *rows, int nrows,
|
||
int scroll_off, int top_on, int top_sw_en)
|
||
{
|
||
const meow_ui_theme_t *th = s_ui_theme();
|
||
int W = fb_xres, H = fb_yres;
|
||
int ypos[SUB_ROWS_MAX + 1];
|
||
sub_ypos(rows, nrows, ypos);
|
||
int total = ypos[nrows];
|
||
int view_h = H - SUB_CONT_TOP;
|
||
int scroll_max = total > view_h ? total - view_h : 0;
|
||
|
||
/* 全屏底 surface + 顶栏区 */
|
||
fill_rect(0, 0, W, H, th->surface);
|
||
draw_statusbar();
|
||
|
||
/* 顶栏:返回箭头 + 标题 + 右上开关;顶栏底缘 outline 分隔线 */
|
||
int tb_cy = SUB_TB_Y + SUB_TB_H / 2; /* 顶栏垂直中心 */
|
||
s_draw_chevron_left(SUB_PAD_X, tb_cy, th->on_surface);
|
||
if (p->title) ui_draw(SUB_PAD_X + 24, tb_cy - meow_font_line_height(22) / 2,
|
||
p->title, th->on_surface, 22);
|
||
if (top_sw_en) {
|
||
int sx = W - SUB_PAD_X - SUB_SW_W;
|
||
int sy = tb_cy - SUB_SW_H / 2;
|
||
s_draw_switch(sx, sy, top_on, th);
|
||
}
|
||
fill_rect(0, SUB_TB_BOT, W, 2, th->outline);
|
||
|
||
/* 内容区(surface_variant 整屏底) */
|
||
fill_rect(0, SUB_TB_BOT, W, H - SUB_TB_BOT, th->surface_variant);
|
||
|
||
/* 分组列表行(裁剪到视口;顶部状态栏/顶栏、底部返回区不画) */
|
||
for (int i = 0; i < nrows; i++) {
|
||
int ry = SUB_CONT_TOP + ypos[i] - scroll_off;
|
||
int rh = sub_row_h(&rows[i]);
|
||
if (ry + rh < SUB_CONT_TOP || ry >= H) continue; /* 视口外整行跳过(配滚动) */
|
||
const sub_row_t *r = &rows[i];
|
||
if (r->kind == SUB_HDR) {
|
||
ui_draw(SUB_PAD_X + 4, ry + (SUB_HDR_H - meow_font_line_height(18)) / 2,
|
||
r->label ? r->label : "", th->on_surface_variant, 18);
|
||
} else {
|
||
int ly = ry + (SUB_ROW_H - meow_font_line_height(22)) / 2;
|
||
ui_draw(SUB_PAD_X + 4, ly, r->label ? r->label : "", th->on_surface, 22);
|
||
if (r->sub && r->sub[0]) {
|
||
int vw = ui_width(r->sub, 22);
|
||
if (r->kind == SUB_ACT) vw += 8; /* 给右 chevron 留位 */
|
||
ui_draw(W - SUB_PAD_X - 12 - vw, ly, r->sub, th->on_surface_variant, 22);
|
||
}
|
||
if (r->kind == SUB_SW) {
|
||
int sy = ry + (SUB_ROW_H - SUB_SW_H) / 2;
|
||
s_draw_switch(W - SUB_PAD_X - SUB_SW_W, sy, r->on, th);
|
||
} else if (r->kind == SUB_ACT) {
|
||
int cy = ry + SUB_ROW_H / 2;
|
||
s_draw_chevron(W - SUB_PAD_X - 6, cy, th->on_surface_variant);
|
||
}
|
||
/* 行底分隔线(on_surface_outline 弱化) */
|
||
meow_ui_fill_rect(SUB_PAD_X + 4, ry + SUB_ROW_H - 1, W - 2 * (SUB_PAD_X + 4), 1,
|
||
th->outline);
|
||
}
|
||
}
|
||
|
||
/* 滚动条(内容超出视口时) */
|
||
if (scroll_max > 0) {
|
||
int thumb_h = view_h * view_h / total;
|
||
if (thumb_h < 24) thumb_h = 24;
|
||
int th_y = SUB_CONT_TOP + (view_h - thumb_h) * scroll_off / (total - view_h);
|
||
meow_gfx_fill_round(W - 12, SUB_CONT_TOP, 5, view_h, 3, th->surface_container);
|
||
meow_gfx_fill_round(W - 12, th_y, 5, thumb_h, 3, th->on_surface_variant);
|
||
}
|
||
|
||
/* 底部返回按钮(左)+ 版本号(右),复用设置页底部对齐 */
|
||
{
|
||
int foot_y = H - meow_font_line_height(20) - 8;
|
||
s_draw_chevron_left(SUB_PAD_X + 4, foot_y + meow_font_line_height(20) / 2,
|
||
th->on_surface_variant);
|
||
ui_draw(SUB_PAD_X + 16, foot_y, "返回", th->on_surface_variant, 20);
|
||
char ver[32];
|
||
snprintf(ver, sizeof ver, "MeowOS v%s", g_meowos_version);
|
||
ui_draw(W - 8 - ui_width(ver, 20), foot_y, ver, th->outline, 20);
|
||
}
|
||
}
|
||
|
||
|
||
/* ---- 运行子页(阻塞到返回;返回 0)---- */
|
||
int meow_subpage_run(sub_page_t *p)
|
||
{
|
||
int W = fb_xres, H = fb_yres;
|
||
sub_row_t rows[SUB_ROWS_MAX];
|
||
int ypos[SUB_ROWS_MAX + 1];
|
||
int nrows = 0, scroll_off = 0, total = 0, view_h = 0, scroll_max = 0;
|
||
|
||
struct keypoll kp_power, kp_keys;
|
||
kp_open(&kp_power, DEV_POWER); kp_open(&kp_keys, DEV_KEYS);
|
||
struct kd_state kd_vold, kd_volu, kd_play, kd_pwr;
|
||
memset(&kd_vold, 0, sizeof kd_vold); memset(&kd_volu, 0, sizeof kd_volu);
|
||
memset(&kd_play, 0, sizeof kd_play); memset(&kd_pwr, 0, sizeof kd_pwr);
|
||
int m_prev_vold = 0, m_prev_volu = 0, m_prev_play = 0, m_prev_pwr = 0;
|
||
for (int w = 0; w < KEYBUF_WORDS; w++) {
|
||
if (kp_keys.cur[w] & (1UL << (KEY_VOLUMEDOWN % 32))) m_prev_vold = 1;
|
||
if (kp_keys.cur[w] & (1UL << (KEY_VOLUMEUP % 32))) m_prev_volu = 1;
|
||
if (kp_keys.cur[w] & (1UL << (KEY_PLAYPAUSE % 32))) m_prev_play = 1;
|
||
if (kp_power.cur[w] & (1UL << (KEY_POWER % 32))) m_prev_pwr = 1;
|
||
}
|
||
int tfd = open(DEV_TOUCH, O_RDONLY | O_NONBLOCK);
|
||
if (tfd >= 0) { unsigned char ev[16]; while (read(tfd, ev, sizeof ev) == 16) {} }
|
||
unsigned long long touch_cooldown_until = now_ms() + 300;
|
||
unsigned long long last_tick = now_ms();
|
||
unsigned long long last_act = now_ms();
|
||
unsigned long long last_live = now_ms();
|
||
int after_modal = 0; /* 子页内模态关闭后:覆盖过状态栏,需整帧重绘还原 */
|
||
|
||
/* 触摸状态 */
|
||
int td = 0, td_ok = 0, tdx = -1, tdy = -1, tlx = -1, tly = -1;
|
||
int scroll_drag = 0, scroll_base = 0, scroll_y0 = 0, scroll_invert = 0;
|
||
int wake_suppress = 0;
|
||
unsigned long long pwr_hold_at = 0;
|
||
int pwr_menu_state = 0, pwr_woke = 0, pwr_suppress_click = 0;
|
||
|
||
/* 顶栏开关初始值 */
|
||
int top_on = 0, top_sw_en = 0;
|
||
if (p->top_sw) top_sw_en = p->top_sw(p->ctx, &top_on);
|
||
|
||
/* 首填行 + 首绘 */
|
||
nrows = p->rows ? p->rows(p->ctx, rows, SUB_ROWS_MAX) : 0;
|
||
if (nrows > SUB_ROWS_MAX) nrows = SUB_ROWS_MAX;
|
||
sub_ypos(rows, nrows, ypos);
|
||
total = ypos[nrows];
|
||
view_h = H - SUB_CONT_TOP;
|
||
scroll_max = total > view_h ? total - view_h : 0;
|
||
subpage_paint(p, rows, nrows, scroll_off, top_on, top_sw_en);
|
||
meow_dirty_full(); meow_ui_flush();
|
||
|
||
for (;;) {
|
||
unsigned long long now = now_ms();
|
||
if (sigterm_flag) break;
|
||
int need_paint = 0;
|
||
|
||
/* ---- 自动息屏态:留在子页,等待任意输入唤醒,恢复后重绘(不丢上下文)---- */
|
||
if (meow_auto_slept && !meow_screen_on) {
|
||
kp_poll(&kp_power, NULL, NULL); kp_poll(&kp_keys, NULL, NULL);
|
||
int wake = 0;
|
||
for (int w = 0; w < KEYBUF_WORDS; w++) {
|
||
if (kp_power.cur[w] & (1UL << (KEY_POWER % 32))) wake = 1;
|
||
if (kp_keys.cur[w] & (1UL << (KEY_VOLUMEDOWN % 32))) wake = 1;
|
||
if (kp_keys.cur[w] & (1UL << (KEY_VOLUMEUP % 32))) wake = 1;
|
||
if (kp_keys.cur[w] & (1UL << (KEY_PLAYPAUSE % 32))) wake = 1;
|
||
}
|
||
if (!wake && screen_off_mode == 1 && tfd >= 0) {
|
||
struct pollfd spfd = { tfd, POLLIN, 0 };
|
||
if (poll(&spfd, 1, 0) > 0) {
|
||
unsigned char ev[16];
|
||
while (read(tfd, ev, sizeof ev) == 16) {
|
||
uint16_t type = *(uint16_t *)(ev + 8), code = *(uint16_t *)(ev + 10);
|
||
int32_t val = *(int32_t *)(ev + 12);
|
||
if (type == EV_KEY && code == BTN_TOUCH && val) wake = 1;
|
||
}
|
||
}
|
||
}
|
||
if (wake) {
|
||
meow_panel_on();
|
||
meow_auto_slept = 0;
|
||
wake_suppress = 1;
|
||
last_act = now;
|
||
memset(&kd_pwr, 0, sizeof kd_pwr); memset(&kd_vold, 0, sizeof kd_vold);
|
||
memset(&kd_volu, 0, sizeof kd_volu); memset(&kd_play, 0, sizeof kd_play);
|
||
kp_poll(&kp_power, NULL, NULL); kp_poll(&kp_keys, NULL, NULL);
|
||
m_prev_pwr = kp_is_down(&kp_power, KEY_POWER);
|
||
m_prev_vold = kp_is_down(&kp_keys, KEY_VOLUMEDOWN);
|
||
m_prev_volu = kp_is_down(&kp_keys, KEY_VOLUMEUP);
|
||
m_prev_play = kp_is_down(&kp_keys, KEY_PLAYPAUSE);
|
||
touch_cooldown_until = now_ms() + 300;
|
||
td = 0; td_ok = 0; scroll_drag = 0; tlx = tly = -1;
|
||
after_modal = 1;
|
||
subpage_paint(p, rows, nrows, scroll_off, top_on, top_sw_en);
|
||
meow_dirty_full(); meow_ui_flush();
|
||
}
|
||
usleep(POLL_MS * 1000);
|
||
continue;
|
||
}
|
||
|
||
/* ---- 按键 ---- */
|
||
kp_poll(&kp_power, NULL, NULL); kp_poll(&kp_keys, NULL, NULL);
|
||
int vold = 0, volu = 0, play = 0, pwr = 0;
|
||
for (int w = 0; w < KEYBUF_WORDS; w++) {
|
||
if (kp_keys.cur[w] & (1UL << (KEY_VOLUMEDOWN % 32))) vold = 1;
|
||
if (kp_keys.cur[w] & (1UL << (KEY_VOLUMEUP % 32))) volu = 1;
|
||
if (kp_keys.cur[w] & (1UL << (KEY_PLAYPAUSE % 32))) play = 1;
|
||
if (kp_power.cur[w] & (1UL << (KEY_POWER % 32))) pwr = 1;
|
||
}
|
||
(void)kd_tick(&kd_vold, vold); (void)kd_tick(&kd_volu, volu);
|
||
(void)kd_tick(&kd_play, play); (void)kd_tick(&kd_pwr, pwr);
|
||
vold = kd_vold.stable; volu = kd_volu.stable;
|
||
play = kd_play.stable; pwr = kd_pwr.stable;
|
||
if (wake_suppress) {
|
||
if (!pwr && !vold && !volu && !play) wake_suppress = 0;
|
||
} else {
|
||
/* 音量键 = 系统音量(对齐设置页,不做光标移动) */
|
||
if (vold && !m_prev_vold) { settings_volume_adj(-settings_volume_step); need_paint = 1; }
|
||
if (volu && !m_prev_volu) { settings_volume_adj(+settings_volume_step); need_paint = 1; }
|
||
if (play && !m_prev_play) { logmsg("subpage: play key caught (media-reserved)"); last_act = now; }
|
||
/* 电源键 = 系统级语义(单击息屏/亮屏、长按电源菜单、8s 强关;不映射返回) */
|
||
if (pwr && !m_prev_pwr) {
|
||
pwr_hold_at = now; pwr_menu_state = 0;
|
||
pwr_suppress_click = 0;
|
||
last_act = now;
|
||
if (meow_auto_slept && !meow_screen_on) pwr_woke = 1;
|
||
}
|
||
if (pwr && pwr_hold_at) {
|
||
if (now - pwr_hold_at >= (unsigned long long)power_forceoff_ms && !pwr_menu_state) {
|
||
if (sim_no_exec) { logmsg("subpage pm: [sim-no-exec] POWER HOLD -> force poweroff"); }
|
||
else { logmsg("subpage pm: POWER HOLD -> force poweroff"); meow_power_action(POWER_SHUTDOWN); }
|
||
} else if (now - pwr_hold_at >= (unsigned long long)power_longpress_menu_ms && !pwr_menu_state) {
|
||
pwr_menu_state = 1;
|
||
logmsg("subpage pm: POWER HOLD -> show power menu");
|
||
show_power_menu();
|
||
last_act = now_ms();
|
||
pwr_hold_at = 0; pwr_menu_state = 0; pwr_woke = 0;
|
||
pwr_suppress_click = 1;
|
||
memset(&kd_pwr, 0, sizeof kd_pwr);
|
||
memset(&kd_vold, 0, sizeof kd_vold);
|
||
memset(&kd_volu, 0, sizeof kd_volu);
|
||
memset(&kd_play, 0, sizeof kd_play);
|
||
kp_poll(&kp_power, NULL, NULL); kp_poll(&kp_keys, NULL, NULL);
|
||
m_prev_pwr = kp_is_down(&kp_power, KEY_POWER);
|
||
m_prev_vold = kp_is_down(&kp_keys, KEY_VOLUMEDOWN);
|
||
m_prev_volu = kp_is_down(&kp_keys, KEY_VOLUMEUP);
|
||
m_prev_play = kp_is_down(&kp_keys, KEY_PLAYPAUSE);
|
||
after_modal = 1;
|
||
need_paint = 1;
|
||
goto sub_keys_seeded;
|
||
}
|
||
}
|
||
if (!pwr && m_prev_pwr) {
|
||
if (pwr_suppress_click) {
|
||
logmsg("subpage pm: power release after menu -> no toggle");
|
||
} else if (pwr_hold_at && (now - pwr_hold_at < (unsigned long long)power_longpress_menu_ms) &&
|
||
!pwr_menu_state && !pwr_woke) {
|
||
logmsg("subpage pm: power single-click -> toggle panel");
|
||
meow_toggle_panel();
|
||
meow_auto_slept = 0;
|
||
last_act = now_ms();
|
||
need_paint = 1;
|
||
} else if (pwr_woke) {
|
||
logmsg("subpage pm: power release after auto wake -> no toggle");
|
||
}
|
||
pwr_hold_at = 0; pwr_menu_state = 0; pwr_woke = 0;
|
||
pwr_suppress_click = 0;
|
||
}
|
||
}
|
||
if (vol_ramp_dir != 0) {
|
||
int held = (vol_ramp_dir > 0) ? volu : vold;
|
||
(void)vol_ramp_tick(held);
|
||
g_sys.vol = settings_volume_get();
|
||
paint_sb_right();
|
||
need_paint = 1;
|
||
}
|
||
m_prev_vold = vold; m_prev_volu = volu; m_prev_play = play; m_prev_pwr = pwr;
|
||
sub_keys_seeded:
|
||
;
|
||
|
||
/* ---- 触摸 ---- */
|
||
if (tfd >= 0 && meow_screen_on) {
|
||
struct pollfd pfd = { tfd, POLLIN, 0 };
|
||
if (poll(&pfd, 1, 0) > 0) {
|
||
unsigned char ev[16];
|
||
while (read(tfd, ev, sizeof ev) == 16) {
|
||
uint16_t type = *(uint16_t *)(ev + 8), code = *(uint16_t *)(ev + 10);
|
||
int32_t val = *(int32_t *)(ev + 12);
|
||
if (type == EV_ABS) {
|
||
if (code == ABS_X || code == ABS_MT_POSITION_X) tlx = val;
|
||
else if (code == ABS_Y || code == ABS_MT_POSITION_Y) tly = val;
|
||
if (td && !td_ok && tlx >= 0 && tly >= 0) {
|
||
tdx = tlx; tdy = tly; td_ok = 1;
|
||
scroll_y0 = tdy;
|
||
}
|
||
if (td_ok && !scroll_drag) {
|
||
int ddy = tly - scroll_y0;
|
||
if (ddy >= S_SCROLL_SLOP || ddy <= -S_SCROLL_SLOP) {
|
||
scroll_drag = 1;
|
||
meow_settings_get_bool("input.swipe_invert", &scroll_invert);
|
||
scroll_base = scroll_off + (scroll_invert ? -ddy : ddy);
|
||
}
|
||
}
|
||
if (scroll_drag && td_ok) {
|
||
int dydt = tly - scroll_y0;
|
||
scroll_off = sub_clamp(scroll_base + (scroll_invert ? dydt : -dydt),
|
||
scroll_max);
|
||
need_paint = 1;
|
||
}
|
||
} else if (type == EV_KEY && code == BTN_TOUCH) {
|
||
if (val && !td) {
|
||
if (now_ms() < touch_cooldown_until) { tlx = tly = -1; }
|
||
else { td = 1; td_ok = 0; tlx = -1; tly = -1; scroll_drag = 0; }
|
||
} else if (!val && td) {
|
||
td = 0;
|
||
if (scroll_drag) {
|
||
scroll_drag = 0; scroll_base = 0; scroll_y0 = 0;
|
||
need_paint = 1;
|
||
} else if (td_ok) {
|
||
int dx = tlx - tdx, dy = tly - tdy;
|
||
/* 左缘左滑返回(复用全局判定) */
|
||
if (meow_edge_back(tdx, dx, dy)) {
|
||
if (tfd >= 0) { unsigned char e[16]; while (read(tfd, e, sizeof e) == 16) {} }
|
||
goto subdone;
|
||
}
|
||
int adx = dx < 0 ? -dx : dx, ady = dy < 0 ? -dy : dy;
|
||
if (adx < 60 && ady < 60) {
|
||
int tx = tdx, ty = tdy;
|
||
/* 底部返回按钮 */
|
||
if (ty >= H - 52 && tx >= SUB_BTN_X && tx <= SUB_BTN_X + SUB_BTN_W) {
|
||
if (tfd >= 0) { unsigned char e[16]; while (read(tfd, e, sizeof e) == 16) {} }
|
||
goto subdone;
|
||
}
|
||
/* 顶栏返回箭头 */
|
||
if (ty >= SUB_TB_Y && ty < SUB_TB_BOT) {
|
||
/* 左上角 48x顶栏高 = 返回箭头 */
|
||
if (tx >= 0 && tx < 48) {
|
||
if (tfd >= 0) { unsigned char e[16]; while (read(tfd, e, sizeof e) == 16) {} }
|
||
goto subdone;
|
||
}
|
||
/* 顶栏开关 */
|
||
if (top_sw_en &&
|
||
tx >= W - SUB_PAD_X - SUB_SW_W - 8 && tx < W &&
|
||
ty >= SUB_TB_Y + (SUB_TB_H - SUB_SW_H) / 2 - 8 &&
|
||
ty < SUB_TB_Y + (SUB_TB_H - SUB_SW_H) / 2 + SUB_SW_H + 8) {
|
||
if (p->top_sw_set) p->top_sw_set(p->ctx, !top_on);
|
||
top_on = !top_on;
|
||
if (p->rows) nrows = p->rows(p->ctx, rows, SUB_ROWS_MAX);
|
||
if (nrows > SUB_ROWS_MAX) nrows = SUB_ROWS_MAX;
|
||
sub_ypos(rows, nrows, ypos);
|
||
total = ypos[nrows];
|
||
scroll_max = total > view_h ? total - view_h : 0;
|
||
if (scroll_off > scroll_max) scroll_off = scroll_max;
|
||
last_act = now;
|
||
need_paint = 1;
|
||
}
|
||
continue;
|
||
}
|
||
/* 普通行命中 */
|
||
int cy = ty - SUB_CONT_TOP + scroll_off;
|
||
int idx = -1;
|
||
for (int i = 0; i < nrows; i++) {
|
||
if (cy >= ypos[i] && cy < ypos[i + 1]) { idx = i; break; }
|
||
}
|
||
if (idx >= 0) {
|
||
sub_kind_t kind = rows[idx].kind;
|
||
if (kind == SUB_SW || kind == SUB_ACT) {
|
||
if (p->act) p->act(p->ctx, idx, rows[idx].act);
|
||
} else if (kind == SUB_ROW) {
|
||
if (p->detail) p->detail(p->ctx, idx, rows[idx].act);
|
||
}
|
||
/* 动作后重扫行:连接/配对/开关已变更数据,列表须反映 */
|
||
if (p->rows) nrows = p->rows(p->ctx, rows, SUB_ROWS_MAX);
|
||
if (nrows > SUB_ROWS_MAX) nrows = SUB_ROWS_MAX;
|
||
sub_ypos(rows, nrows, ypos);
|
||
total = ypos[nrows];
|
||
scroll_max = total > view_h ? total - view_h : 0;
|
||
if (scroll_off > scroll_max) scroll_off = scroll_max;
|
||
}
|
||
if (idx >= 0) {
|
||
/* 回调可能开模态(s_dd_modal 用独立 fd 消费);返回后彻底
|
||
* 排空本页 tfd + 重置触摸基线,防穿透(复刻设置页后置排空)。 */
|
||
if (tfd >= 0) {
|
||
unsigned char e[16];
|
||
while (read(tfd, e, sizeof e) == 16) {}
|
||
usleep(40000);
|
||
while (read(tfd, e, sizeof e) == 16) {}
|
||
}
|
||
td = 0; td_ok = 0; scroll_drag = 0;
|
||
scroll_base = 0; scroll_y0 = 0;
|
||
tdx = tdy = tlx = tly = -1;
|
||
touch_cooldown_until = now_ms() + 300;
|
||
memset(&kd_pwr, 0, sizeof kd_pwr);
|
||
memset(&kd_vold, 0, sizeof kd_vold);
|
||
memset(&kd_volu, 0, sizeof kd_volu);
|
||
memset(&kd_play, 0, sizeof kd_play);
|
||
kp_poll(&kp_power, NULL, NULL); kp_poll(&kp_keys, NULL, NULL);
|
||
m_prev_pwr = kp_is_down(&kp_power, KEY_POWER);
|
||
m_prev_vold = kp_is_down(&kp_keys, KEY_VOLUMEDOWN);
|
||
m_prev_volu = kp_is_down(&kp_keys, KEY_VOLUMEUP);
|
||
m_prev_play = kp_is_down(&kp_keys, KEY_PLAYPAUSE);
|
||
after_modal = 1;
|
||
need_paint = 1;
|
||
}
|
||
}
|
||
}
|
||
td_ok = 0;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* ---- 周期性重扫(live 页:蓝牙附近扫描等驻留更新)---- */
|
||
if (p->live && now - last_live >= 2000) {
|
||
last_live = now;
|
||
int new_n = p->rows ? p->rows(p->ctx, rows, SUB_ROWS_MAX) : 0;
|
||
if (new_n > SUB_ROWS_MAX) new_n = SUB_ROWS_MAX;
|
||
if (new_n != nrows) {
|
||
nrows = new_n;
|
||
sub_ypos(rows, nrows, ypos);
|
||
total = ypos[nrows];
|
||
scroll_max = total > view_h ? total - view_h : 0;
|
||
if (scroll_off > scroll_max) scroll_off = scroll_max;
|
||
last_act = now;
|
||
need_paint = 1;
|
||
}
|
||
}
|
||
|
||
/* ---- 重绘 ---- */
|
||
if (need_paint) {
|
||
last_act = now;
|
||
subpage_paint(p, rows, nrows, scroll_off, top_on, top_sw_en);
|
||
if (after_modal) { meow_dirty_full(); after_modal = 0; }
|
||
else meow_dirty_add(0, SUB_TB_Y, fb_xres, fb_yres - SUB_TB_Y); /* 含顶栏(开关/返回/标题) */
|
||
meow_ui_flush();
|
||
}
|
||
|
||
/* ---- 自动息屏(空闲超时关屏,留在子页,等输入唤醒)---- */
|
||
if (screen_off_timeout > 0 && meow_screen_on && !meow_auto_slept &&
|
||
(unsigned long long)(now - last_act) >= (unsigned long long)screen_off_timeout * 1000) {
|
||
meow_panel_off();
|
||
meow_auto_slept = 1;
|
||
wake_suppress = 1;
|
||
logmsg("subpage: idle %ds -> auto screen-off (stay)", screen_off_timeout);
|
||
}
|
||
/* ~1s 状态栏时钟刷新 */
|
||
if (now - last_tick >= 1000) {
|
||
last_tick = now;
|
||
draw_clock();
|
||
fb_norm_buf0();
|
||
meow_ui_flush();
|
||
}
|
||
usleep(POLL_MS * 1000);
|
||
}
|
||
subdone:
|
||
if (tfd >= 0) close(tfd);
|
||
kp_close(&kp_power); kp_close(&kp_keys);
|
||
logmsg("subpage '%s' closed", p->title ? p->title : "");
|
||
return 0;
|
||
}
|