Files
MeowOS-For-R3P2/patch_v2/meow_launcherd/subpage.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

505 lines
26 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.
/* 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;
}