feat: M6.40批 — 阅读器UI改版(书架list·grid+空态骨架+界面设置+App设置页+header·footer+背景黑默认) + 系统级音量翻页映射 + P1 MD3(select面板28/对话框按钮胶囊) + MeowPanel写端点护栏 + SDK公开仓·文档同步
- reader: app.c/shelf.c/reader.c 全套UI; 新增 reader_conf(reader.conf)/settings(App设置页)/reader_ipc(音量翻页IPC响应); app.json加volume_page - launcher: keys.c meow_volpage_*门 + util.c app_id→fd映射(HELLO记fd/断开清) + app_launcher.c 前台三处接线(进入sink/按键路径volpage_key/离开clear) → 前台声明volume_page的App音量键→MSG_KEY_PAGEUP/PAGEDOWN翻页(非阅读器前台音量照旧) - sdk: meow_select.c 底部面板28 + meow_ui_dialog.c 对话框按钮胶囊(h/2) + test_meow_ui_select.c修采样 - meowpanel: /api/net/policy·/api/power/policy 加防自锁+policy默认关两条护栏(panel.policy_enabled默认0) - 文档: MEOW_APP_SDK.md §12.6 + meowpanel README端点表 0warning;宿主测试全过;二进制留批量批次未入库
This commit is contained in:
1 parent
b364b6371a
commit
2826c1e905
27 files changed
+1704
-129
No files matched your search
@@ -1929,6 +1929,8 @@ static int launch_app_chain(struct app *a, const char *chain)
|
||||
int nav_clean_exit = 0; /* M3:MSG_NAV{EXIT} 主动退出 → 不按崩溃上报 */
|
||||
int to_background = 0; /* M4:nav-exit 转后台保活(进程保留,跳过退出尾) */
|
||||
int app_prev_vold = 0;
|
||||
int app_prev_volu = 0; /* E:VOL_UP 上一边沿(翻页门下边沿判定) */
|
||||
int fg_volpage = 0; /* E:前台 App 是否声明 volume_page(会话期恒定) */
|
||||
int app_seq_hits = 0;
|
||||
unsigned long long app_seq_last = 0;
|
||||
struct vol_snap app_vol0 = { -1, -1 };
|
||||
@@ -1968,6 +1970,13 @@ static int launch_app_chain(struct app *a, const char *chain)
|
||||
* 清残留 pending(防上一会话未消费的 OPEN 被本会话误展开)。 */
|
||||
g_app_session_active = 1;
|
||||
meow_notif_open_clear();
|
||||
/* E:系统级音量翻页——前台 App 声明 volume_page 时武装 sink(fd 就绪即生效;断线重连由
|
||||
* 本循环每圈重试 meow_volpage_sink 幂等更新,keys.c 门只消费 VOL 按下且已武装)。 */
|
||||
fg_volpage = meow_volpage_app_allows(a->id) ? 1 : 0;
|
||||
if (fg_volpage) {
|
||||
int vfd = meow_ipc_app_fd(a->id);
|
||||
if (vfd >= 0) meow_volpage_sink(a->id, vfd);
|
||||
}
|
||||
for (;;) {
|
||||
int r = waitpid(pid, &status, WNOHANG);
|
||||
if (r == pid) { ams_status_valid = 1; break; }
|
||||
@@ -2057,6 +2066,21 @@ static int launch_app_chain(struct app *a, const char *chain)
|
||||
for (int w = 0; w < KEYBUF_WORDS; w++) {
|
||||
if (kp_keys.cur[w] & (1UL << (KEY_VOLUMEDOWN % 32))) vold = 1;
|
||||
}
|
||||
/* E:系统级音量翻页——前台 App 声明 volume_page 时,VOL_UP/VOL_DOWN 按下边沿 →
|
||||
* 系统翻页信号(keys.c 门,已武装才消费下发;未武装返回 0 放行,音量/物理键语义全保留)。
|
||||
* 每圈幂等重试武装(reader 持久连接 HELLO 后 fd 就绪;断线重连换 fd 自动更新)。 */
|
||||
if (fg_volpage) {
|
||||
int vfd = meow_ipc_app_fd(a->id);
|
||||
if (vfd >= 0) meow_volpage_sink(a->id, vfd);
|
||||
}
|
||||
{
|
||||
int volu = 0;
|
||||
for (int w = 0; w < KEYBUF_WORDS; w++)
|
||||
if (kp_keys.cur[w] & (1UL << (KEY_VOLUMEUP % 32))) volu = 1;
|
||||
if (volu && !app_prev_volu) (void)meow_volpage_key(KEY_VOLUMEUP, 1);
|
||||
if (vold && !app_prev_vold) (void)meow_volpage_key(KEY_VOLUMEDOWN, 1);
|
||||
app_prev_volu = volu;
|
||||
}
|
||||
/* M2.17 自动息屏(app 会话):任意物理键边沿重置计时;空闲>timeout 且非 keep_awake → 息屏;
|
||||
* 息屏后再按键 → 唤醒(fb_pan_buf0_force 兜底 LCDC 落 buffer0,内容随应用输入重绘)。
|
||||
* M6.5 §310 问题1:watchdog 电源键手动息屏(背光 0、不置 meow_auto_slept)→ 唤醒时按
|
||||
@@ -2223,6 +2247,7 @@ static int launch_app_chain(struct app *a, const char *chain)
|
||||
if (to_background) {
|
||||
g_app_session_active = 0; /* 前台会话结束(ipc_dispatch 不再受理 NOTIF_OPEN) */
|
||||
meow_notif_open_clear();
|
||||
meow_volpage_sink_clear(); /* E:离开前台撤翻页 sink(音量键交还系统) */
|
||||
if (wpid > 0) {
|
||||
/* 看门狗随前台会话结束回收(后台无手势监听;有界等防挂死) */
|
||||
kill(wpid, SIGTERM);
|
||||
@@ -2251,6 +2276,7 @@ static int launch_app_chain(struct app *a, const char *chain)
|
||||
/* §10.5:App 前台会话结束,清会话标记(ipc_dispatch 不再受理 MSG_NOTIF_OPEN) */
|
||||
g_app_session_active = 0;
|
||||
meow_notif_open_clear();
|
||||
meow_volpage_sink_clear(); /* E:离开前台撤翻页 sink(音量键交还系统) */
|
||||
/* M2.17 兜底:App 会话结束(waitpid 回收/崩溃/被杀,回主界面)清 fg_act ——
|
||||
* App 在 keep_awake activity(如 ncm 播放页)被 OOM/崩溃杀死且未弹回 home 时,g_fg_act 会
|
||||
* 滞留 "<act>",使主页 fg_keep_awake()==1 不再自动息屏。此处兜底清空(App 已退出/死亡,
|
||||
|
||||
@@ -212,3 +212,92 @@ void load_config(void)
|
||||
menu_bg, seq_len, seq_codes[0], seq_gap_ms);
|
||||
}
|
||||
|
||||
/* =========================================================================
|
||||
* 系统级音量键翻页门(READER_UI_DESIGN §E;用户 2026-09-04 拍板)。
|
||||
*
|
||||
* 语义:音量键是系统资源(默认调音量)。本门在「当前台 App 为其 app.json 声明
|
||||
* volume_page 的应用(MVP:os.meow.reader)」时,把 VOL_UP/VOL_DOWN 转换成系统翻页
|
||||
* 信号(MSG_KEY_PAGEUP/PAGEDOWN,meow_ipc.h)经 IPC 下发给该前台 App;其余情况一概
|
||||
* 放行(return 0),音量基本行为/插孔消抖/音量反转等绝不破坏。
|
||||
*
|
||||
* 职责分离(关键,且最小侵入):
|
||||
* - 本模块只提供「判定 + 门」:meow_volpage_app_allows() 读 app.json 声明;
|
||||
* meow_volpage_key() 在「已武装 sink 且按下 VOL」时消费并发下发;
|
||||
* - 武装(sink 注册)= 前台应用进入体积页声明 app 时调用 meow_volpage_sink()
|
||||
* (应用存在连接后其 fd 生效);离开/退出时 meow_volpage_sink_clear()。
|
||||
* 该接线落在「前台会话的按键路径」(app_launcher.c / meow_launcherd.c 主循环),
|
||||
* 属主会话统一收编范围,此处不越界改动。
|
||||
*
|
||||
* 声明字段(app.json):`"volume_page": true`(或 1)。未来其它 App 可声明复用。
|
||||
* ========================================================================= */
|
||||
|
||||
/* 前台翻页 sink(本模块私有;单写者=前台会话上下文,读=按键门)。 */
|
||||
static char g_volpage_app[MEOW_APP_ID_MAX] = "";
|
||||
static int g_volpage_fd = -1;
|
||||
|
||||
void meow_volpage_sink_clear(void); /* 前向声明(sink 内调用) */
|
||||
|
||||
/* 读 <MEOWAPPS>/<app_id>/app.json 是否声明 volume_page=true|1。1=声明;0=否/读不到。 */
|
||||
int meow_volpage_app_allows(const char *app_id)
|
||||
{
|
||||
if (!app_id || !app_id[0]) return 0;
|
||||
char path[320];
|
||||
snprintf(path, sizeof path, "%s/%s/app.json", MEOWAPPS, app_id);
|
||||
FILE *f = fopen(path, "r");
|
||||
if (!f) return 0;
|
||||
/* 轻量扫描:找 "volume_page" → 跳过空白 → true/1。app.json 为紧凑 JSON。 */
|
||||
char buf[1024];
|
||||
buf[0] = 0;
|
||||
size_t rd = fread(buf, 1, sizeof buf - 1, f);
|
||||
buf[rd] = 0;
|
||||
fclose(f);
|
||||
const char *p = buf;
|
||||
while ((p = strstr(p, "volume_page")) != NULL) {
|
||||
p += strlen("volume_page");
|
||||
while (*p == ' ' || *p == '\t' || *p == ':' || *p == '"') p++;
|
||||
if (strncmp(p, "true", 4) == 0) return 1;
|
||||
if (*p == '1' && (p[1] < '0' || p[1] > '9')) return 1;
|
||||
/* 未命中则继续找(防字段重复);跳过本段 */
|
||||
while (*p && *p != ',' && *p != '}') p++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 武装/清除 sink。app_id 非空且 fd>=0 → 记录;否则清除。幂等(同 app_id+fd 不重复记日志,
|
||||
* 供前台会话循环每圈安全重调,也兼容断线重连换 fd)。 */
|
||||
void meow_volpage_sink(const char *app_id, int fd)
|
||||
{
|
||||
if (!app_id || !app_id[0] || fd < 0) { meow_volpage_sink_clear(); return; }
|
||||
if (g_volpage_fd == fd && !strcmp(g_volpage_app, app_id)) return; /* 同 app_id+fd:幂等 */
|
||||
snprintf(g_volpage_app, sizeof g_volpage_app, "%s", app_id);
|
||||
g_volpage_fd = fd;
|
||||
logmsg("volpage: sink armed app=%s fd=%d", g_volpage_app, g_volpage_fd);
|
||||
}
|
||||
|
||||
void meow_volpage_sink_clear(void)
|
||||
{
|
||||
if (g_volpage_fd >= 0)
|
||||
logmsg("volpage: sink cleared app=%s", g_volpage_app[0] ? g_volpage_app : "?");
|
||||
g_volpage_app[0] = 0;
|
||||
g_volpage_fd = -1;
|
||||
}
|
||||
|
||||
int meow_volpage_armed(void) { return g_volpage_fd >= 0; }
|
||||
|
||||
/* 按键门:按下 VOL_UP/DOWN 且 sink 已武装 → 下发翻页信号并消费(返回 1)。
|
||||
* 未武装 / 非 VOL / 抬起 → 返回 0(放行给音量路径)。 */
|
||||
int meow_volpage_key(int code, int is_down)
|
||||
{
|
||||
if (!is_down) return 0; /* 只响应按下边沿 */
|
||||
if (g_volpage_fd < 0) return 0; /* 未武装:音量照旧 */
|
||||
if (code == KEY_VOLUMEUP) {
|
||||
(void)meow_ipc_send_pub(g_volpage_fd, MSG_KEY_PAGEUP, 1, NULL, 0);
|
||||
return 1;
|
||||
}
|
||||
if (code == KEY_VOLUMEDOWN) {
|
||||
(void)meow_ipc_send_pub(g_volpage_fd, MSG_KEY_PAGEDOWN, 1, NULL, 0);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -82,6 +82,14 @@ enum {
|
||||
MSG_META = 0x0072, /* ↑ 媒体会话上报元数据/进度/状态(阶段 D 附录 B) */
|
||||
MSG_PLAYCTL = 0x0073, /* ↓ 播放控制下发/↑ 控制器转发到最近活跃媒体会话 */
|
||||
MSG_META_LYRIC = 0x0074, /* ↑ 媒体会话上报当前曲歌词 lrc/tlrc(阶段 D · 批3,分片) */
|
||||
/* ---- 系统翻页信号(READER_UI_DESIGN §E,用户 2026-09-04 拍板):0x0080-0x0081 ----
|
||||
* 语义:launcher 按键层(meow_launcherd/keys.c)在「当前台 App 为其 app.json 声明
|
||||
* volume_page 的前台应用」时,把物理 VOL_UP/VOL_DOWN 转换成系统翻页信号下发(否则照旧
|
||||
* 调音量——音量基本行为/插孔消抖/音量反转绝不破坏)。App 侧(如 reader_app/app/reader_ipc.c)
|
||||
* 持持久连接收本消息 → 上一页/下一页。方向由消息号本身决定(UP=上一页,DOWN=下一页);
|
||||
* 无负载(len=0)。属系统级能力,未来其它 App 可声明复用。 */
|
||||
MSG_KEY_PAGEUP = 0x0080, /* ↓ 系统级:上一页(VOL_UP 语义,仅前台声明 app 收) */
|
||||
MSG_KEY_PAGEDOWN = 0x0081, /* ↓ 系统级:下一页(VOL_DOWN 语义,仅前台声明 app 收) */
|
||||
/* ---- amsd 控制(AMSD_DESIGN §4.2):amsd→launcher,申请拉起/终止某 App ----
|
||||
* 语义:amsd 为「启动/终止」决策者 → launcher(执行器)执行。payload 见下。 */
|
||||
MSG_AMS_START = 0x00A0, /* → 请求 launcher 拉起某 App(pkg + 可选 activity) */
|
||||
|
||||
@@ -756,6 +756,15 @@ int meow_ipc_conn_alive(int fd);
|
||||
/* M4:ipc_send 公开包装(meow_bgplay send_frame 回调用;同 meow_media 的注入口径)。 */
|
||||
size_t meow_ipc_send_pub(int fd, uint16_t type, uint32_t req_id,
|
||||
const void *payload, uint32_t len);
|
||||
/* ---- E:系统级音量翻页(READER_UI_DESIGN §E,用户 2026-09-04 拍板)----
|
||||
* 前台 App 声明 app.json `volume_page` 时,launcher 把物理 VOL_UP/DOWN 转成系统翻页信号
|
||||
* (MSG_KEY_PAGEUP/PAGEDOWN,meow_ipc.h)经 IPC 下发给该 App;否则照旧调音量。 */
|
||||
int meow_volpage_app_allows(const char *app_id); /* keys.c:读 app.json 声明 */
|
||||
void meow_volpage_sink(const char *app_id, int fd); /* keys.c:武装前台翻页 sink */
|
||||
void meow_volpage_sink_clear(void); /* keys.c:撤武装 */
|
||||
int meow_volpage_armed(void); /* keys.c:查询 */
|
||||
int meow_volpage_key(int code, int is_down); /* keys.c:门(1=已消费/已下发) */
|
||||
int meow_ipc_app_fd(const char *app_id); /* util.c:前台 App→持久 IPC fd(-1=无) */
|
||||
/* §10.5:读并清除待处理的 MSG_NOTIF_OPEN(App 前台顶部下滑请求展开通知中心叠加会话)。
|
||||
* 调用方=launch_app waitpid 循环;返回 1 并填 *out(待处理),0=无。 */
|
||||
int meow_notif_open_take(m_notif_open_t *out);
|
||||
|
||||
@@ -907,6 +907,52 @@ int meow_ipc_conn_alive(int fd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---- E:系统级音量翻页——app_id→持久 IPC fd 最近活跃映射(util.c 单写者 = ipc_dispatch)----
|
||||
* App 的「持久连接」(如 reader_app/app/reader_ipc.c 后台线程)连上后发 MSG_HELLO,本映射在该
|
||||
* HELLO 被 ipc_dispatch 处理时记录 app_id→m->fd;连接关闭(meow_ipc_process 剔除)时清对应项。
|
||||
* 只记录「declares volume_page」的 App(meow_volpage_app_allows),保持映射极小且无歧义。
|
||||
* meow_ipc_app_fd() 供 app_launcher.c 前台会话取持久 fd 武装翻页 sink。 */
|
||||
#define VOLPAGE_FD_MAX 8
|
||||
static struct { char app_id[MEOW_APP_ID_MAX]; int fd; } g_volpage_fd[VOLPAGE_FD_MAX];
|
||||
static int g_volpage_fd_n;
|
||||
|
||||
static void volpage_fd_set(const char *app_id, int fd)
|
||||
{
|
||||
if (!app_id || !app_id[0] || fd < 0) return;
|
||||
for (int i = 0; i < g_volpage_fd_n; i++) {
|
||||
if (!strcmp(g_volpage_fd[i].app_id, app_id)) { g_volpage_fd[i].fd = fd; return; }
|
||||
}
|
||||
if (g_volpage_fd_n < VOLPAGE_FD_MAX) {
|
||||
snprintf(g_volpage_fd[g_volpage_fd_n].app_id, sizeof g_volpage_fd[0].app_id, "%s", app_id);
|
||||
g_volpage_fd[g_volpage_fd_n].fd = fd;
|
||||
g_volpage_fd_n++;
|
||||
}
|
||||
}
|
||||
|
||||
static void volpage_fd_clear_fd(int fd)
|
||||
{
|
||||
for (int i = 0; i < g_volpage_fd_n; i++) {
|
||||
if (g_volpage_fd[i].fd == fd) {
|
||||
for (int j = i; j < g_volpage_fd_n - 1; j++) g_volpage_fd[j] = g_volpage_fd[j + 1];
|
||||
g_volpage_fd_n--;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int meow_ipc_app_fd(const char *app_id)
|
||||
{
|
||||
if (!app_id) return -1;
|
||||
for (int i = 0; i < g_volpage_fd_n; i++) {
|
||||
if (!strcmp(g_volpage_fd[i].app_id, app_id)) {
|
||||
if (meow_ipc_conn_alive(g_volpage_fd[i].fd)) return g_volpage_fd[i].fd;
|
||||
volpage_fd_clear_fd(g_volpage_fd[i].fd); /* fd 已死:清残留 */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* §10.5:待处理的 App 前台「展开通知中心」请求(ipc_dispatch 单写者置位;launch_app waitpid
|
||||
* 循环 meow_notif_open_take 读取并清除)。仅当 g_app_session_active(App 前台会话)期间才置位,
|
||||
* 否则回 ERR(无 App 前台,不展开)。 */
|
||||
@@ -1099,6 +1145,7 @@ static void *ipc_thread(void *arg)
|
||||
if (ipc_conn_read(cc) < 0) {
|
||||
logmsg("ipc: conn %d closed", cc->fd);
|
||||
close(cc->fd);
|
||||
volpage_fd_clear_fd(cc->fd); /* E:清已断连接的翻页 fd 映射 */
|
||||
for (int j = i; j < nfds - 1; j++) {
|
||||
fds[j] = fds[j + 1];
|
||||
ipc_conns[j - 1] = ipc_conns[j];
|
||||
@@ -1209,6 +1256,8 @@ static void ipc_dispatch(struct meow_ipc_msg *m)
|
||||
memcpy(&h, m->payload, m->plen < (uint16_t)sizeof h ? m->plen : (uint16_t)sizeof h);
|
||||
h.app_id[MEOW_APP_ID_MAX - 1] = 0;
|
||||
meow_acct_hello(h.app_id, h.version, h.flags);
|
||||
/* E:记录声明 volume_page 的 App 的持久连接 fd(HELLO=App 侧持久 IPC 连接;仅读卡 app.json 一次)。 */
|
||||
if (meow_volpage_app_allows(h.app_id)) volpage_fd_set(h.app_id, m->fd);
|
||||
m_ack_t a;
|
||||
a.code = MEOW_OK;
|
||||
a.status = (uint32_t)g_naccts;
|
||||
|
||||
Reference in new issue
Block a user