feat(sdk+launcher): SDK nav/media封装 + §10.5 App前台唤起通知中心 (agent-206) — meow_ui_nav.c(env栈初始化+report/request_exit) + meow_ui_media.c(懒连断线重连, meta上报+playctl接收) + MSG_NOTIF_OPEN=0x28/CLOSE=0x29(0x25/26已被占); SDK顶部下滑手势→IPC; launcher App前台会话叠加通知中心(SIGSTOP→读帧→接管输入→恢复SIGCONT, 面板内App崩溃即关); hello接线验证

meow_ui.a→9a8eb7ef; hello→fc299577; launcher→b33da20a; 宿主5套件+新test_notif_open+qemu分发 全过
出入: SDK符号用meow_ui_前缀防与launcher内同名冲突; 叠加会话真机待验; §10.5文档旧号段已过时
This commit is contained in:
MeowOS committed 2026-08-31 07:53:57 +08:00
1 parent 21519ce2e4
commit 9b4195724c
37 files changed
+1042 -21

No files matched your search

+106
View File
@@ -7,6 +7,8 @@ static struct meow_acct g_accts[MEOW_MAX_ACTS]; /* 主循环单写者 */
int g_naccts = 0;
volatile int g_app_session_active = 0; /* §10.5:1=launch_app waitpid 前台会话中(util.c 读) */
/* ---- 阶段 D · M1:路由注册表辅助(主循环单写者,无锁)---- */
struct meow_acct *meow_acct_find(const char *app_id)
@@ -504,6 +506,23 @@ static int proc_is_stopped(int pid)
return p[2] == 'T';
}
/* /proc/pid/stat 状态位 == 'Z'(僵尸):子进程已死、launcher 未回收。§10.5:App 前台叠加
* 会话期间据此「立即结束」通知中心面板(不回收,回收仍由 waitpid 循环统一做)。 */
int proc_is_zombie(int pid)
{
char path[64], line[512];
snprintf(path, sizeof path, "/proc/%d/stat", pid);
int fd = open(path, O_RDONLY);
if (fd < 0) return 0;
ssize_t n = read(fd, line, sizeof line - 1);
close(fd);
if (n <= 0) return 0;
line[n] = 0;
char *p = strrchr(line, ')');
if (!p || p + 2 >= line + n) return 0;
return p[2] == 'Z';
}
int stop_player(void)
{
@@ -881,6 +900,66 @@ static void notify_app_crash(const char *pkg, int status)
close(fd);
}
/* §10.5:App 前台顶部下滑 → 展开「由 launcher 渲染」的通知中心叠加会话。
* 复用电源菜单对 App 的冻结模板(show_power_menu 的 SIGSTOP/读回/叠加/接管/恢复):
* ① SIGSTOP App → ② 写 /tmp/meow_notif_active(+ /tmp/meow_power_menu_active 让 watchdog 让位)
* → ③ pm_capture_app_frame 读回 App 帧当背景 → ④ run_notif_center(apps={a},1) 叠加渲染
* + 接管输入 → ⑤ 关闭后恢复 App 帧 → unlink active → SIGCONT App。
* 差异:背景=App 帧(非应用列表);关闭后回 App(非回 run_menu);点通知首期 = dismiss + 关面板
* + SIGCONT 回当前 App(不跨 app 启动,见 NOTIF_CENTER_APP_OVERLAY_PLAN §3.2)。
* App 崩溃探测:run_notif_center 循环内 read /proc/<pid>/stat 状态位(zombie)周期探测
* (g_notif_ctrl_pid),发现死 → g_notif_ctrl_died → 立即结束会话(不 SIGCONT 死进程、不恢复死帧);
* 回收仍由 waitpid 循环统一做(捕获真实 exit 状态);返回 1 让 waitpid 循环走统一应用退出路径
* 归位(launcher 不崩)。返回 0=正常回 App。 */
static int run_app_notif_overlay(struct app *a, pid_t pid)
{
if (!back_buf_ok || !fb) { logmsg("notif-overlay: no back_buf (direct mode) skip"); return 0; }
logmsg("notif-overlay: open (app=%s pid=%d) SIGSTOP", a->name, pid);
if (kill(pid, SIGSTOP) < 0) {
logmsg("notif-overlay: SIGSTOP %d fail: %s", pid, strerror(errno));
} else {
/* 等 App 进入 stopped 态(/proc/pid/stat 状态位 'T';超时兜底);frozen 后才能读回帧 */
for (int i = 0; i < 100; i++) {
if (proc_is_stopped(pid)) break;
usleep(10000);
}
}
/* 让位标记:/tmp/meow_notif_active(§10.5 语义同 powmenu_active)。当前 watchdog 只认
* POWMENU_ACTIVE,故同时写它让 watchdog 对电源长按让位;会话结束统一 unlink。 */
int nfd = open(NOTIF_ACTIVE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (nfd >= 0) { write(nfd, "1", 1); close(nfd); }
int pfd = open(POWMENU_ACTIVE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (pfd >= 0) { write(pfd, "1", 1); close(pfd); }
meow_sys_light(); /* 顶条数据新鲜(电量/WiFi/音量/蓝牙) */
meow_theme_reload(); /* SDK 状态栏主题随 launcher 换肤 */
pm_capture_app_frame(); /* 读回 App 帧(fb0 buffer0)→ back_buf 当背景 */
struct app app1[1];
app1[0] = *a;
notif_click_t click;
g_notif_ctrl_pid = (int)pid; /* run_notif_center 周期探测 App 存活 */
g_notif_ctrl_died = 0;
run_notif_center(app1, 1, &click, 0, 0, 0);
g_notif_ctrl_pid = 0;
int died = g_notif_ctrl_died;
unlink(NOTIF_ACTIVE);
unlink(POWMENU_ACTIVE);
if (died) {
/* App 已死:不 SIGCONT、不恢复死帧;back_buf 交还 run_menu 统一重绘 */
logmsg("notif-overlay: app died during session -> unified exit path");
return 1;
}
/* 恢复 App 帧(run_notif_center 关面板后 back_buf 已还原为背景=App 帧;再拍一次兜底
* 覆盖非动画关闭路径)+ SIGCONT。 */
pm_capture_app_frame();
meow_ui_flush();
kill(pid, SIGCONT);
logmsg("notif-overlay: closed -> SIGCONT app");
return 0;
}
/* launch_app_chain:launch_app / launch_activity 的共享核心。
* chain 非空 → 向子进程注入 MEOW_ACTIVITY/MEOW_CHAIN 并初始化 nav_stack(M3);空/NULL → 裸启动。 */
static int launch_app_chain(struct app *a, const char *chain)
@@ -1133,6 +1212,10 @@ static int launch_app_chain(struct app *a, const char *chain)
/* M2.24:清上一次会话可能残留的电源菜单标记,只认本次新 watchdog 生成的请求。 */
unlink(POWMENU_MARKER);
unlink(POWMENU_ACTIVE);
/* §10.5:进入 App 前台会话。置位让 ipc_dispatch 的 MSG_NOTIF_OPEN 可入队(否则回 ERR);
* 清残留 pending(防上一会话未消费的 OPEN 被本会话误展开)。 */
g_app_session_active = 1;
meow_notif_open_clear();
for (;;) {
int r = waitpid(pid, &status, WNOHANG);
if (r == pid) break;
@@ -1149,6 +1232,26 @@ static int launch_app_chain(struct app *a, const char *chain)
if (waitpid(pid, &status, WNOHANG) != pid) { kill(pid, SIGKILL); waitpid(pid, &status, 0); }
break;
}
/* §10.5:App 前台顶部下滑(MSG_NOTIF_OPEN)→ 展开通知中心叠加会话。复用电源菜单
* SIGSTOP/读回/叠加/接管/恢复模板;App 死 → run_app_notif_overlay 返回 1,走统一退出。 */
{
m_notif_open_t nopen;
if (meow_notif_open_take(&nopen)) {
int nr = run_app_notif_overlay(a, pid);
/* 输入基线重播(与电源菜单同款):面板期间按键可能已变化,重播防误判 key_edge */
kp_poll(&kp_power, NULL, NULL);
kp_poll(&kp_keys, NULL, NULL);
memcpy(save_pow, kp_power.cur, sizeof save_pow);
memcpy(save_keys, kp_keys.cur, sizeof save_keys);
app_last_input = now_ms(); /* overlay 交互即输入,重置 App 会话息屏计时 */
if (nr != 0) {
/* App 会话中崩溃(overlay 内 proc_is_zombie 探测):回收僵尸取真实状态,
* 走统一应用退出路径(不 SIGCONT 死进程、不恢复死帧;launcher 不崩)。 */
waitpid(pid, &status, 0);
break;
}
}
}
/* M2.11 D:卡被拔(设备节点消失)且前台应用在卡上 → 主动 SIGTERM 干净退出,
* 避免应用等 SIGBUS 崩。回菜单走正常应用退出路径。 */
if (app_on_card && !sd_device_present()) {
@@ -1273,6 +1376,9 @@ static int launch_app_chain(struct app *a, const char *chain)
/* 顶条收合/恢复过渡期把循环睡得短些(~20ms),驱动更平滑的上移出屏;稳定后回 100ms。 */
usleep(meow_ui_statusbar_animating() ? 20000 : 100000);
}
/* §10.5:App 前台会话结束,清会话标记(ipc_dispatch 不再受理 MSG_NOTIF_OPEN) */
g_app_session_active = 0;
meow_notif_open_clear();
/* M2.x:App 异常退出(信号杀 / 非 0 退出码)→ best-effort 直报 meow_crash。
* 判据与 svcd reap_children 一致:failed = !(WIFEXITED && WEXITSTATUS==0)。干净 exit(0)
* 不上报;sigterm 关停/电源菜单重启路径 status 保持 0(未 waitpid)→ 视为干净退出不上报。 */
Binary file not shown.
+65 -2
View File
@@ -419,6 +419,57 @@ static int read_frame(int fd, uint16_t *type, uint32_t *req_id, uint8_t *rbuf, u
return (int)len;
}
/* §10.5:App 前台顶部下滑 → 请求展开通知中心叠加会话 → MSG_NOTIF_OPEN,期望 MSG_ACK。
* notifopen <app_id> [origin_y] [hotzone] */
static int cmd_notifopen(const char *app_id, uint16_t origin_y, uint16_t hotzone)
{
int fd = connect_path(g_sock);
if (fd < 0) return 1;
m_notif_open_t o;
memset(&o, 0, sizeof o);
strncpy(o.app_id, app_id, MEOW_APP_ID_MAX - 1);
o.event_seq = 1;
o.origin_x = 0;
o.origin_y = origin_y;
o.hotzone = hotzone;
uint8_t rbuf[256];
int rt = xchg(fd, MSG_NOTIF_OPEN, 20, &o, sizeof o, rbuf, sizeof rbuf);
close(fd);
if (rt == MSG_ACK) {
m_ack_t a;
memcpy(&a, rbuf, sizeof a);
printf("NOTIF_OPEN ok: app=%s oy=%u hz=%u code=%d status=%u\n",
app_id, origin_y, hotzone, a.code, a.status);
return a.code == MEOW_OK ? 0 : 1;
}
if (rt == MSG_ERR) { print_err((const m_err_t *)rbuf); return 1; }
fprintf(stderr, "NOTIF_OPEN: unexpected resp type 0x%04x\n", rt);
return 1;
}
/* §10.5:App 告知结束/退出 → MSG_NOTIF_CLOSE(launcher 收尾兜底),期望 MSG_ACK。
* notifclose <app_id> <reason> */
static int cmd_notifclose(const char *app_id, uint32_t reason)
{
int fd = connect_path(g_sock);
if (fd < 0) return 1;
m_notif_close_t o;
memset(&o, 0, sizeof o);
strncpy(o.app_id, app_id, MEOW_APP_ID_MAX - 1);
o.reason = reason;
uint8_t rbuf[256];
int rt = xchg(fd, MSG_NOTIF_CLOSE, 21, &o, sizeof o, rbuf, sizeof rbuf);
close(fd);
if (rt == MSG_ACK) {
m_ack_t a;
memcpy(&a, rbuf, sizeof a);
printf("NOTIF_CLOSE ok: app=%s reason=%u code=%d\n", app_id, reason, a.code);
return a.code == MEOW_OK ? 0 : 1;
}
if (rt == MSG_ERR) { print_err((const m_err_t *)rbuf); return 1; }
fprintf(stderr, "NOTIF_CLOSE: unexpected resp type 0x%04x\n", rt);
return 1;
}
/* 媒体源监听:HELLO(MF_PLAYBACK) + META → 保持连接等待 launcher 下发 MSG_PLAYCTL。
* listen <app_id> <state> <pos_ms> <dur_ms> <title> <artist> <album> [artwork] */
static int cmd_listen(const char *app_id, uint32_t state, uint32_t pos, uint32_t dur,
@@ -472,8 +523,7 @@ int main(int argc, char **argv)
for (; i < argc; i++) {
if (!strcmp(argv[i], "--sock") && i + 1 < argc) { g_sock = argv[++i]; }
else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
fprintf(stderr, "usage: meow_ipc [--sock <path>] hello|ping|query|declare ...\n");
return 0;
fprintf(stderr, "usage: meow_ipc [--sock <path>] hello|ping|query|declare ...\n"); return 0;
}
else break;
}
@@ -552,6 +602,19 @@ int main(int argc, char **argv)
int64_t seek = (i + 2 < argc) ? (int64_t)strtoll(argv[i + 2], NULL, 0) : 0;
return cmd_playctl(cmd2, seek);
}
if (!strcmp(cmd, "notifopen")) {
/* notifopen <app_id> [origin_y] [hotzone] */
if (i + 1 >= argc) { fprintf(stderr, "notifopen needs <app_id>\n"); return 1; }
uint16_t oy = (i + 2 < argc) ? (uint16_t)strtoul(argv[i + 2], NULL, 0) : 0;
uint16_t hz = (i + 3 < argc) ? (uint16_t)strtoul(argv[i + 3], NULL, 0) : 28;
return cmd_notifopen(argv[i + 1], oy, hz);
}
if (!strcmp(cmd, "notifclose")) {
/* notifclose <app_id> <reason> */
if (i + 1 >= argc) { fprintf(stderr, "notifclose needs <app_id>\n"); return 1; }
uint32_t r2 = (i + 2 < argc) ? (uint32_t)strtoul(argv[i + 2], NULL, 0) : 0;
return cmd_notifclose(argv[i + 1], r2);
}
if (!strcmp(cmd, "nav")) {
/* nav report <app_id> <depth> <top> | nav exit <app_id> */
if (i + 1 >= argc) { fprintf(stderr, "nav needs <app_id> and report|exit\n"); return 1; }
+24
View File
@@ -51,6 +51,8 @@ enum {
MSG_NOTIFY_QUERY = 0x0025, /* ↑ 查询内存活跃通知集(MeowPanel 实时化前置) */
MSG_NOTIFY_DISMISS_ALL = 0x0026,/* ↑ 一键清除(保留 ongoing/media/progress 进行中,同通知中心 UI) */
MSG_NOTIFY_QUERY_RESULT = 0x0027, /* ↓ 活跃通知集序列化文本(JSON,字段同 TF 归档) */
MSG_NOTIF_OPEN = 0x0028, /* ↑ App 前台顶部下滑 → 请求 launcher 展开通知中心叠加会话(§10.5) */
MSG_NOTIF_CLOSE = 0x0029, /* ↑ App 告知结束/退出 → launcher 收尾兜底(可选用途,§10.5) */
MSG_ACT_DECLARE = 0x0030, /* ↑ (重)声明路由表 */
MSG_ACT_QUERY = 0x0031, /* ↑ 解析 activity 链路 */
MSG_ACT_RESULT = 0x0032, /* ↓ 返回链路 */
@@ -243,6 +245,28 @@ typedef struct m_notify_query_result {
char text[MEOW_IPC_MAX - 12]; /* 序列化 JSON 数组 */
} m_notify_query_result_t; /* 4096 B */
/* ================= App 前台划出通知中心(FRAMEWORK_DESIGN §10.5) = */
/* MSG_NOTIF_OPEN ↑(App→launcher):App 进程内顶部下滑(MEOW_EV_NOTIF_PULL),请求 launcher
* 展开通知中心叠加会话。App 只检测+上报、**不渲染系统 UI**(MEOW_APP_SDK §6.1 ③)。
* 负载字段全 LE 定长。 */
typedef struct m_notif_open {
char app_id[MEOW_APP_ID_MAX]; /* 32 */
uint32_t event_seq; /* App 侧事件序号(防重放/去抖,可选;0=填充默认) */
uint16_t origin_x; /* 手势按下原点 X(防御性校验:应≈顶部,best-effort 可 0) */
uint16_t origin_y; /* 手势按下原点 Y(launcher 复核是否在顶部热区) */
uint16_t hotzone; /* 上报时 SDK 的热区高(28 或 4,见 meow_ui_statusbar_hotzone) */
uint8_t flags; /* 预留:位0=App 声明 fullscreen */
uint8_t pad;
} m_notif_open_t; /* 32+4+2+2+2+1+1 = 44 B(LE,4 字节对齐) */
/* MSG_NOTIF_CLOSE ↑(App→launcher,可选用途):App 告知结束/退出,launcher 收尾兜底
* (清 active 标记/SIGCONT 兜底)。非可靠链路:launcher 主要靠 waitpid/kill(pid,0) 自感知。 */
typedef struct m_notif_close {
char app_id[MEOW_APP_ID_MAX]; /* 32 */
uint32_t reason; /* 0=App 主动退出;1=App 请求关闭通知中心;2=App 崩溃 */
} m_notif_close_t; /* 32+4 = 36 B */
/* ================= 专项服务 IPC(SYSTEM_FRAMEWORK_SERVICES §2.2/§2.3)= */
/* ---- MSG_PERF_QUERY 子集位(0=全部)---- */
@@ -51,6 +51,9 @@ extern "C" {
#define WATCHDOG2 "/usr/bin/meow_watchdog"
#define POWMENU_MARKER "/tmp/meow_power_menu"
#define POWMENU_ACTIVE "/tmp/meow_power_menu_active"
/* §10.5:launcher 展开通知中心叠加会话的让位标记(App 前台;同 /tmp/meow_power_menu_active 语义,
* 让 watchdog 对电源长按让位——当前 watchdog 只认 POWMENU_ACTIVE,故叠加会话期间同时写它,见 app_launcher.c) */
#define NOTIF_ACTIVE "/tmp/meow_notif_active"
#define BACKLIGHT "/sys/class/backlight/backlight_pwm0/brightness"
#define BACKLIGHT_POWER "/sys/class/backlight/backlight_pwm0/bl_power"
#define FB_DEV "/dev/fb0"
@@ -559,6 +562,10 @@ extern int meow_screen_on;
extern int mfont_forced;
extern int mfont_ready;
extern int player_stopped;
/* §10.5:App 前台叠加通知中心会话的跨模块状态(app_launcher.c 置位;util.c ipc_dispatch 读) */
extern volatile int g_app_session_active; /* 1=launch_app waitpid 前台会话中 */
extern int g_notif_ctrl_pid; /* >0=run_notif_center 处于 App 叠加会话,周期探测 App 存活 */
extern int g_notif_ctrl_died; /* 1=会话中 App 已死 → 立即结束会话走统一退出路径 */
extern const int pm_cx[PM_NUM];
extern int power_forceoff_ms;
extern int power_longpress_menu_ms;
@@ -653,6 +660,11 @@ void meow_font_scan_presets(void);
void meow_font_scan_presets_root(const char *card_root);
int meow_ipc_init(void);
void meow_ipc_process(void);
/* §10.5:读并清除待处理的 MSG_NOTIF_OPEN(App 前台顶部下滑请求展开通知中心叠加会话)。
* 调用方=launch_app waitpid 循环;返回 1 并填 *out(待处理),0=无。 */
int meow_notif_open_take(m_notif_open_t *out);
/* §10.5:清残留待处理 OPEN(进入新 App 前台会话前调用,防旧会话残留误展开)。 */
void meow_notif_open_clear(void);
void meow_panel_off(void);
void meow_panel_on(void);
void meow_parse_app_json(FILE *f, struct app *a);
@@ -717,6 +729,9 @@ int show_power_menu(void);
int stop_player(void);
/* 电源管理服务 IPC(SYSTEM_FRAMEWORK_SERVICES §3.2:launcher 电源动作经 meow_svc_power 执行) */
int pm_power_ipc(uint32_t cmd, uint32_t flag, const char *arg, char *res, int reslen);
/* §10.5:/proc/<pid>/stat 状态位 == 'Z'(僵尸子进程=已死未回收)。notif_center 叠加会话用它
* 立即感知 App 崩溃(waitpid 回收仍由 app_launcher waitpid 循环统一做)。 */
int proc_is_zombie(int pid);
int meow_power_action(uint32_t cmd);
void ui_draw(int x, int y, const char *s, uint32_t color, int size);
int ui_width(const char *s, int size);
Binary file not shown.
+15
View File
@@ -11,6 +11,12 @@ static struct app *g_ncp_apps = NULL; /* run_notif_center 注入(通知点
static int g_ncp_napps = 0;
/* §10.5:App 前台叠加会话期间,run_notif_center 周期探测该 App 是否存活(kill(pid,0))。
* >0 = 正在探测(app_launcher.c run_app_notif_overlay 置位);发现 ESRCH → g_notif_ctrl_died=1
* 立即结束会话(不 SIGCONT 死进程、不恢复死帧),让前台 waitpid 循环走统一退出路径。 */
int g_notif_ctrl_pid = 0;
int g_notif_ctrl_died = 0;
/* ⑥ 勿扰面板内开关:当前 dnd 态(开面板/切换时从 meow_settings notify.dnd 读/写,零旁路)。
* ncp_collect_cb / ncp_active_count 按它过滤行(only urgency>=NCP_DND_MIN_URGENCY when on)。 */
@@ -1041,6 +1047,15 @@ void run_notif_center(struct app *apps, int napps, notif_click_t *click,
for (;;) {
unsigned long long now = now_ms();
if (sigterm_flag) break;
/* §10.5:App 前台叠加会话期间探测 App 存活。App 死(/proc/<pid>/stat 状态位 Z=僵尸)→
* 立即结束会话(回 run_menu 统一重绘背景,不 SIGCONT 死进程、不恢复死帧);launcher 不崩。
* 注:读 /proc 状态位而非 waitpid(WNOHANG)——waitpid 一旦检测到终止即回收子进程,会抢走
* app_launcher waitpid 循环的回收权(其状态/exit 码经统一路径处理),此处只读不回收。 */
if (g_notif_ctrl_pid > 0 && proc_is_zombie(g_notif_ctrl_pid)) {
logmsg("notif-center: app pid %d died during overlay -> close now", g_notif_ctrl_pid);
g_notif_ctrl_died = 1;
break;
}
int animating = anim_ok && (anim_p != anim_target);
/* 自动息屏(同设置页语义) */
+61
View File
@@ -829,6 +829,28 @@ static pthread_t ipc_pthread;
static struct meow_ipc_conn ipc_conns[MEOW_IPC_CONNS];
/* §10.5:待处理的 App 前台「展开通知中心」请求(ipc_dispatch 单写者置位;launch_app waitpid
* 循环 meow_notif_open_take 读取并清除)。仅当 g_app_session_active(App 前台会话)期间才置位,
* 否则回 ERR(无 App 前台,不展开)。 */
static m_notif_open_t g_notif_open;
static volatile int g_notif_open_pending = 0;
int meow_notif_open_take(m_notif_open_t *out)
{
if (!out) return 0;
if (!g_notif_open_pending) return 0;
*out = g_notif_open;
g_notif_open_pending = 0;
return 1;
}
/* 会话边界:进入新 App 前台会话前清残留,防旧会话未消费的 OPEN 被新会话误展开。 */
void meow_notif_open_clear(void)
{
g_notif_open_pending = 0;
memset(&g_notif_open, 0, sizeof g_notif_open);
}
/* 写一帧(16B 头 + 负载)。返回写入字节;<=0 视为失败。 */
static size_t ipc_send(int fd, uint16_t type, uint32_t req_id,
@@ -1255,6 +1277,45 @@ static void ipc_dispatch(struct meow_ipc_msg *m)
logmsg("ipc: NOTIFY_DISMISS_ALL -> %d cleared", rc);
break;
}
/* §10.5:App 前台顶部下滑 → 请求展开通知中心叠加会话。仅在 App 前台会话(launch_app
* waitpid 循环)期间受理(置 pending 让该循环消费后展开叠加);否则回 ACK(status=BUSY)
* 让客户端知情但不展开(主循环/无 App 前台不展开)。App 不强依赖本 ACK(best-effort)。 */
case MSG_NOTIF_OPEN: {
m_notif_open_t o;
memset(&o, 0, sizeof o);
memcpy(&o, m->payload, m->plen < (uint16_t)sizeof o ? m->plen : (uint16_t)sizeof o);
o.app_id[MEOW_APP_ID_MAX - 1] = 0;
m_ack_t a;
a.code = MEOW_OK;
a.status = 0;
if (g_app_session_active && !g_notif_open_pending) {
g_notif_open = o; /* 单写者置位;waitpid 循环 take 后展开 */
g_notif_open_pending = 1;
a.status = 1; /* 已受理入队 */
logmsg("ipc: NOTIF_OPEN app=%s oy=%u hz=%u -> queued", o.app_id, o.origin_y, o.hotzone);
} else {
a.status = 0xFFFFFFFFu; /* 无 App 前台 / 已有会话在跑:不展开 */
logmsg("ipc: NOTIF_OPEN app=%s oy=%u hz=%u -> ignored (no app session or busy)",
o.app_id, o.origin_y, o.hotzone);
}
ipc_send(m->fd, MSG_ACK, m->req_id, &a, sizeof a);
break;
}
/* §10.5:App 告知结束/退出 → launcher 收尾兜底(清 pending/标记;非可靠链路,launcher 主要
* 靠 waitpid/kill(pid,0) 自感知)。best-effort,回 ACK。 */
case MSG_NOTIF_CLOSE: {
m_notif_close_t o;
memset(&o, 0, sizeof o);
memcpy(&o, m->payload, m->plen < (uint16_t)sizeof o ? m->plen : (uint16_t)sizeof o);
o.app_id[MEOW_APP_ID_MAX - 1] = 0;
meow_notif_open_clear();
m_ack_t a;
a.code = MEOW_OK;
a.status = (uint32_t)o.reason;
ipc_send(m->fd, MSG_ACK, m->req_id, &a, sizeof a);
logmsg("ipc: NOTIF_CLOSE app=%s reason=%u -> ACK", o.app_id, o.reason);
break;
}
case MSG_NOTIFY_LIST:
/* ↓ 守护→App 下发通知清单(重同步)。本帧不来自客户端(无请求类型),故不在此应答;
* 需用时由守护主动经 meow_notif_serialize_list() + ipc_send 下发(M5 通知中心接入)。 */