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:
1 parent
21519ce2e4
commit
9b4195724c
37 files changed
+1042
-21
No files matched your search
@@ -8,6 +8,8 @@
|
|||||||
* 契约(MEOW_APP_SDK.md §1/§3/§6):应用只调 meow_ui API。
|
* 契约(MEOW_APP_SDK.md §1/§3/§6):应用只调 meow_ui API。
|
||||||
*/
|
*/
|
||||||
#include "hello_internal.h"
|
#include "hello_internal.h"
|
||||||
|
#include "meow_ui_nav.h" /* ①:App 侧 M3 导航上报封装(上报页面深度/顶层) */
|
||||||
|
#include "meow_ui_media.h" /* ②:媒体会话封装(demo 页按钮上报 META + 收 PLAYCTL) */
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
||||||
/* Dialog / BottomSheet(阻塞模态;非控件,main() 创建一次,demo 页打开/关) */
|
/* Dialog / BottomSheet(阻塞模态;非控件,main() 创建一次,demo 页打开/关) */
|
||||||
@@ -20,6 +22,24 @@ static meow_view *g_demo_view;
|
|||||||
|
|
||||||
static void on_sigterm(int s) { (void)s; meow_ui_request_exit(); }
|
static void on_sigterm(int s) { (void)s; meow_ui_request_exit(); }
|
||||||
|
|
||||||
|
/* ② 媒体会话播放控制回调(launcher 下发 MSG_PLAYCTL 到最活跃媒体会话)。 */
|
||||||
|
static void demo_media_playctl(int cmd, int64_t seek_ms)
|
||||||
|
{
|
||||||
|
meow_log(MEOW_LOG_INFO, "media playctl cmd=%d seek=%lld", cmd, (long long)seek_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ① 导航上报:把当前 Activity 栈深/顶层报给 launcher(M3 导航栈)。每个页面 on_enter 时调用
|
||||||
|
* (view_page_push/pop 会同步触发新页 on_enter),保证 pop/push 后 launcher 的 nav_stack 跟随。
|
||||||
|
* depth 取 meow_ui_activity_depth()(实际 push 页数,≥1);top 取 activity id 映射的字符串。 */
|
||||||
|
void hello_nav_report(void)
|
||||||
|
{
|
||||||
|
int depth = meow_ui_activity_depth();
|
||||||
|
if (depth <= 0) depth = 1;
|
||||||
|
int id = meow_ui_activity_active();
|
||||||
|
const char *top = (id == ACT_FULL) ? "full" : (id == ACT_DEMO) ? "demo" : "home";
|
||||||
|
meow_ui_nav_report((uint32_t)depth, top);
|
||||||
|
}
|
||||||
|
|
||||||
/* ============================================================ */
|
/* ============================================================ */
|
||||||
/* 事件分发:meow_ui_run 平铺回调 → 当前页 */
|
/* 事件分发:meow_ui_run 平铺回调 → 当前页 */
|
||||||
/* ============================================================ */
|
/* ============================================================ */
|
||||||
@@ -79,6 +99,11 @@ int main(void)
|
|||||||
meow_ui_opts o = { 480, 720 };
|
meow_ui_opts o = { 480, 720 };
|
||||||
if (meow_ui_init(&o) != 0) { meow_log(MEOW_LOG_ERROR, "init failed"); return 1; }
|
if (meow_ui_init(&o) != 0) { meow_log(MEOW_LOG_ERROR, "init failed"); return 1; }
|
||||||
meow_theme_add_listener(NULL);
|
meow_theme_add_listener(NULL);
|
||||||
|
meow_ui_nav_init(); /* ① 读 MEOW_ACTIVITY/MEOW_CHAIN 初始化导航栈 */
|
||||||
|
meow_ui_media_init(NULL); /* ② app_id=env MEOW_LOG_APP */
|
||||||
|
|
||||||
|
/* ② 注册播放控制回调(launcher 经媒体会话下发 MSG_PLAYCTL):hello 只打日志验证。 */
|
||||||
|
meow_ui_media_on_playctl(demo_media_playctl);
|
||||||
|
|
||||||
signal(SIGTERM, on_sigterm);
|
signal(SIGTERM, on_sigterm);
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -114,4 +114,7 @@ extern rect_t r_tb, r_nav, r_fab;
|
|||||||
void chrome_create(void); /* 建 chrome(调用方须先 push ACT_DEMO 使 owner=ACT_DEMO) */
|
void chrome_create(void); /* 建 chrome(调用方须先 push ACT_DEMO 使 owner=ACT_DEMO) */
|
||||||
void chrome_free(void); /* 释放 chrome(demo on_destroy 调) */
|
void chrome_free(void); /* 释放 chrome(demo on_destroy 调) */
|
||||||
|
|
||||||
|
/* ---- App 级导航/媒体接线(app.c)---- */
|
||||||
|
void hello_nav_report(void); /* 上报当前 Activity 栈深/顶层(各页 on_enter 调) */
|
||||||
|
|
||||||
#endif /* HELLO_INTERNAL_H */
|
#endif /* HELLO_INTERNAL_H */
|
||||||
@@ -14,12 +14,14 @@
|
|||||||
* UI→业务只经 logic_* / notify_*。页面进入/离开经 meow_view_page_push/pop 驱动。
|
* UI→业务只经 logic_* / notify_*。页面进入/离开经 meow_view_page_push/pop 驱动。
|
||||||
*/
|
*/
|
||||||
#include "hello_internal.h"
|
#include "hello_internal.h"
|
||||||
|
#include "meow_ui_media.h" /* ② demo 页按钮上报媒体 META + 收 PLAYCTL */
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
/* 供 demo_content_tap / touch 回调用的前向声明 */
|
/* 供 demo_content_tap / touch 回调用的前向声明 */
|
||||||
static void demo_slider_drag(int x);
|
static void demo_slider_drag(int x);
|
||||||
|
static void demo_media_send(void);
|
||||||
|
|
||||||
static demo_ctrl g_demo_ctrl;
|
static demo_ctrl g_demo_ctrl;
|
||||||
|
|
||||||
@@ -86,6 +88,8 @@ static meow_widget *g_dbadge; /* Badge */
|
|||||||
static meow_widget *g_dtab; static rect_t r_tab; /* TabBar */
|
static meow_widget *g_dtab; static rect_t r_tab; /* TabBar */
|
||||||
static meow_widget *g_dbtn_dlg; static rect_t r_dlg; /* 打开 Dialog 的按钮 */
|
static meow_widget *g_dbtn_dlg; static rect_t r_dlg; /* 打开 Dialog 的按钮 */
|
||||||
static meow_widget *g_dbtn_sheet; static rect_t r_sheet;/* 打开 BottomSheet 的按钮 */
|
static meow_widget *g_dbtn_sheet; static rect_t r_sheet;/* 打开 BottomSheet 的按钮 */
|
||||||
|
/* ② 媒体会话上报按钮(demo 页演示):点按 → meow_ui_media_meta 上报一条播放中元数据。 */
|
||||||
|
static meow_widget *g_dbtn_media; static rect_t r_media;
|
||||||
/* LazyColumn:g_dlc/r_list 已迁 list.c(列表区 CONTAINER 子视图),此处仅 extern 引用。 */
|
/* LazyColumn:g_dlc/r_list 已迁 list.c(列表区 CONTAINER 子视图),此处仅 extern 引用。 */
|
||||||
|
|
||||||
/* ---- demo 分区标题/slider 值文本:注册为 CUSTOM 绘制物(§6.2 区域重画会重画它们)----
|
/* ---- demo 分区标题/slider 值文本:注册为 CUSTOM 绘制物(§6.2 区域重画会重画它们)----
|
||||||
@@ -235,6 +239,10 @@ static void demo_build_at(int scroll)
|
|||||||
r_sheet = RX(DCR_X, SY(yb), DCW, DBH); g_dbtn_sheet = meow_btn_create_variant(r_sheet.x, r_sheet.y, r_sheet.w, r_sheet.h, "SHEET", MEOW_BTN_OUTLINED); SLOT(g_dbtn_sheet, r_sheet.x, r_sheet.y, r_sheet.w, r_sheet.h);
|
r_sheet = RX(DCR_X, SY(yb), DCW, DBH); g_dbtn_sheet = meow_btn_create_variant(r_sheet.x, r_sheet.y, r_sheet.w, r_sheet.h, "SHEET", MEOW_BTN_OUTLINED); SLOT(g_dbtn_sheet, r_sheet.x, r_sheet.y, r_sheet.w, r_sheet.h);
|
||||||
yb += DCH;
|
yb += DCH;
|
||||||
|
|
||||||
|
/* ---- Media Session(② demo 按钮上报媒体 META,验证 SDK 封装) ---- */
|
||||||
|
d_title(DCL_X, SY(yb), "MEDIA SESSION (meta)"); yb += 26;
|
||||||
|
r_media = RX(DCL_X, SY(yb), DCW, DBH); g_dbtn_media = meow_btn_create_variant(r_media.x, r_media.y, r_media.w, r_media.h, "SEND META", MEOW_BTN_FILLED); SLOT(g_dbtn_media, r_media.x, r_media.y, r_media.w, r_media.h); yb += DCH;
|
||||||
|
|
||||||
/* ---- LazyColumn(100 项列表滚动) ----
|
/* ---- LazyColumn(100 项列表滚动) ----
|
||||||
* 本区块标题改由列表区 CONTAINER 子视图(ui/demo/list/list.c)的 on_draw 绘制(注册表→
|
* 本区块标题改由列表区 CONTAINER 子视图(ui/demo/list/list.c)的 on_draw 绘制(注册表→
|
||||||
* invalidate 链路);列表滚动/选中由 list_view_bind 从子视图持有态恢复(不再经 g_ds)。 */
|
* invalidate 链路);列表滚动/选中由 list_view_bind 从子视图持有态恢复(不再经 g_ds)。 */
|
||||||
@@ -310,9 +318,21 @@ static int demo_content_tap(int x, int y)
|
|||||||
}
|
}
|
||||||
if (meow_widget_hit(g_dbtn_dlg, x, y)) { meow_dialog_show(g_dlg); meow_log(MEOW_LOG_INFO, "dialog result=%d", meow_dialog_result(g_dlg)); meow_ui_invalidate(r_dlg.x, r_dlg.y, r_dlg.w, r_dlg.h); return 1; }
|
if (meow_widget_hit(g_dbtn_dlg, x, y)) { meow_dialog_show(g_dlg); meow_log(MEOW_LOG_INFO, "dialog result=%d", meow_dialog_result(g_dlg)); meow_ui_invalidate(r_dlg.x, r_dlg.y, r_dlg.w, r_dlg.h); return 1; }
|
||||||
if (meow_widget_hit(g_dbtn_sheet, x, y)) { int r = meow_sheet_open(g_sheet); meow_log(MEOW_LOG_INFO, "sheet result=%d", r); meow_ui_invalidate(r_sheet.x, r_sheet.y, r_sheet.w, r_sheet.h); return 1; }
|
if (meow_widget_hit(g_dbtn_sheet, x, y)) { int r = meow_sheet_open(g_sheet); meow_log(MEOW_LOG_INFO, "sheet result=%d", r); meow_ui_invalidate(r_sheet.x, r_sheet.y, r_sheet.w, r_sheet.h); return 1; }
|
||||||
|
if (meow_widget_hit(g_dbtn_media, x, y)) { demo_media_send(); meow_ui_invalidate(r_media.x, r_media.y, r_media.w, r_media.h); return 1; }
|
||||||
return 0; /* 列表区分发已由顶部 list_view_hit 处理(子>父) */
|
return 0; /* 列表区分发已由顶部 list_view_hit 处理(子>父) */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ② 经 SDK 媒体会话封装上报一条「播放中」元数据(best-effort:无 launcher 静默降级)。
|
||||||
|
* 上报后 launcher(通知中心媒体卡片/主界面 Now Playing 页)可显示;demo 在 on_playctl 回调
|
||||||
|
* 打日志验证 receive 侧。 */
|
||||||
|
static void demo_media_send(void)
|
||||||
|
{
|
||||||
|
meow_ui_media_meta(1 /* playing */, 0, 180000,
|
||||||
|
"MeowOS Demo Track", "SDK", "meow_ui_media",
|
||||||
|
NULL, NULL);
|
||||||
|
meow_log(MEOW_LOG_INFO, "demo: media meta sent (playing, 0/180000)");
|
||||||
|
}
|
||||||
|
|
||||||
/* demo 页 on_tap 分发(chrome → 内容) */
|
/* demo 页 on_tap 分发(chrome → 内容) */
|
||||||
void demo_on_tap(int x, int y)
|
void demo_on_tap(int x, int y)
|
||||||
{
|
{
|
||||||
@@ -408,6 +428,7 @@ static void demo_page_enter(meow_view *v, void *ctx)
|
|||||||
((demo_ctrl *)ctx)->active = 1;
|
((demo_ctrl *)ctx)->active = 1;
|
||||||
g_demo = 1;
|
g_demo = 1;
|
||||||
g_demo_focus = 0;
|
g_demo_focus = 0;
|
||||||
|
hello_nav_report(); /* ① 上报 demo 栈深/顶层 */
|
||||||
/* 列表区 CONTAINER 子视图随本页进入由 SDK view_enter 递归驱动(其 on_draw 在 demo_rebuild_to
|
/* 列表区 CONTAINER 子视图随本页进入由 SDK view_enter 递归驱动(其 on_draw 在 demo_rebuild_to
|
||||||
* →demo_paint 中经注册表绘制;meow_draw_view_entered 含祖先已进入判定,首批可见)。 */
|
* →demo_paint 中经注册表绘制;meow_draw_view_entered 含祖先已进入判定,首批可见)。 */
|
||||||
demo_rebuild_to(0);
|
demo_rebuild_to(0);
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ static void full_page_enter(meow_view *v, void *ctx)
|
|||||||
g_full = 1;
|
g_full = 1;
|
||||||
meow_ui_set_fullscreen(1);
|
meow_ui_set_fullscreen(1);
|
||||||
meow_ui_statusbar_set_collapsed(1);
|
meow_ui_statusbar_set_collapsed(1);
|
||||||
|
hello_nav_report(); /* ① 上报 full 栈深/顶层 */
|
||||||
full_draw();
|
full_draw();
|
||||||
meow_log(MEOW_LOG_INFO, "fullscreen ON (collapsed), activity->%d", meow_ui_activity_active());
|
meow_log(MEOW_LOG_INFO, "fullscreen ON (collapsed), activity->%d", meow_ui_activity_active());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ static void home_page_enter(meow_view *v, void *ctx)
|
|||||||
{
|
{
|
||||||
(void)v;
|
(void)v;
|
||||||
((home_ctrl *)ctx)->active = 1;
|
((home_ctrl *)ctx)->active = 1;
|
||||||
|
hello_nav_report(); /* ① 上报 home 栈深/顶层(pop 回 home / 首次进入) */
|
||||||
home_redraw();
|
home_redraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ static struct meow_acct g_accts[MEOW_MAX_ACTS]; /* 主循环单写者 */
|
|||||||
|
|
||||||
int g_naccts = 0;
|
int g_naccts = 0;
|
||||||
|
|
||||||
|
volatile int g_app_session_active = 0; /* §10.5:1=launch_app waitpid 前台会话中(util.c 读) */
|
||||||
|
|
||||||
|
|
||||||
/* ---- 阶段 D · M1:路由注册表辅助(主循环单写者,无锁)---- */
|
/* ---- 阶段 D · M1:路由注册表辅助(主循环单写者,无锁)---- */
|
||||||
struct meow_acct *meow_acct_find(const char *app_id)
|
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';
|
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)
|
int stop_player(void)
|
||||||
{
|
{
|
||||||
@@ -881,6 +900,66 @@ static void notify_app_crash(const char *pkg, int status)
|
|||||||
close(fd);
|
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 的共享核心。
|
/* launch_app_chain:launch_app / launch_activity 的共享核心。
|
||||||
* chain 非空 → 向子进程注入 MEOW_ACTIVITY/MEOW_CHAIN 并初始化 nav_stack(M3);空/NULL → 裸启动。 */
|
* chain 非空 → 向子进程注入 MEOW_ACTIVITY/MEOW_CHAIN 并初始化 nav_stack(M3);空/NULL → 裸启动。 */
|
||||||
static int launch_app_chain(struct app *a, const char *chain)
|
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 生成的请求。 */
|
/* M2.24:清上一次会话可能残留的电源菜单标记,只认本次新 watchdog 生成的请求。 */
|
||||||
unlink(POWMENU_MARKER);
|
unlink(POWMENU_MARKER);
|
||||||
unlink(POWMENU_ACTIVE);
|
unlink(POWMENU_ACTIVE);
|
||||||
|
/* §10.5:进入 App 前台会话。置位让 ipc_dispatch 的 MSG_NOTIF_OPEN 可入队(否则回 ERR);
|
||||||
|
* 清残留 pending(防上一会话未消费的 OPEN 被本会话误展开)。 */
|
||||||
|
g_app_session_active = 1;
|
||||||
|
meow_notif_open_clear();
|
||||||
for (;;) {
|
for (;;) {
|
||||||
int r = waitpid(pid, &status, WNOHANG);
|
int r = waitpid(pid, &status, WNOHANG);
|
||||||
if (r == pid) break;
|
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); }
|
if (waitpid(pid, &status, WNOHANG) != pid) { kill(pid, SIGKILL); waitpid(pid, &status, 0); }
|
||||||
break;
|
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 干净退出,
|
/* M2.11 D:卡被拔(设备节点消失)且前台应用在卡上 → 主动 SIGTERM 干净退出,
|
||||||
* 避免应用等 SIGBUS 崩。回菜单走正常应用退出路径。 */
|
* 避免应用等 SIGBUS 崩。回菜单走正常应用退出路径。 */
|
||||||
if (app_on_card && !sd_device_present()) {
|
if (app_on_card && !sd_device_present()) {
|
||||||
@@ -1273,6 +1376,9 @@ static int launch_app_chain(struct app *a, const char *chain)
|
|||||||
/* 顶条收合/恢复过渡期把循环睡得短些(~20ms),驱动更平滑的上移出屏;稳定后回 100ms。 */
|
/* 顶条收合/恢复过渡期把循环睡得短些(~20ms),驱动更平滑的上移出屏;稳定后回 100ms。 */
|
||||||
usleep(meow_ui_statusbar_animating() ? 20000 : 100000);
|
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。
|
/* M2.x:App 异常退出(信号杀 / 非 0 退出码)→ best-effort 直报 meow_crash。
|
||||||
* 判据与 svcd reap_children 一致:failed = !(WIFEXITED && WEXITSTATUS==0)。干净 exit(0)
|
* 判据与 svcd reap_children 一致:failed = !(WIFEXITED && WEXITSTATUS==0)。干净 exit(0)
|
||||||
* 不上报;sigterm 关停/电源菜单重启路径 status 保持 0(未 waitpid)→ 视为干净退出不上报。 */
|
* 不上报;sigterm 关停/电源菜单重启路径 status 保持 0(未 waitpid)→ 视为干净退出不上报。 */
|
||||||
|
|||||||
Binary file not shown.
@@ -419,6 +419,57 @@ static int read_frame(int fd, uint16_t *type, uint32_t *req_id, uint8_t *rbuf, u
|
|||||||
return (int)len;
|
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。
|
/* 媒体源监听:HELLO(MF_PLAYBACK) + META → 保持连接等待 launcher 下发 MSG_PLAYCTL。
|
||||||
* listen <app_id> <state> <pos_ms> <dur_ms> <title> <artist> <album> [artwork] */
|
* 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,
|
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++) {
|
for (; i < argc; i++) {
|
||||||
if (!strcmp(argv[i], "--sock") && i + 1 < argc) { g_sock = argv[++i]; }
|
if (!strcmp(argv[i], "--sock") && i + 1 < argc) { g_sock = argv[++i]; }
|
||||||
else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
|
else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
|
||||||
fprintf(stderr, "usage: meow_ipc [--sock <path>] hello|ping|query|declare ...\n");
|
fprintf(stderr, "usage: meow_ipc [--sock <path>] hello|ping|query|declare ...\n"); return 0;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
else break;
|
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;
|
int64_t seek = (i + 2 < argc) ? (int64_t)strtoll(argv[i + 2], NULL, 0) : 0;
|
||||||
return cmd_playctl(cmd2, seek);
|
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")) {
|
if (!strcmp(cmd, "nav")) {
|
||||||
/* nav report <app_id> <depth> <top> | nav exit <app_id> */
|
/* 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; }
|
if (i + 1 >= argc) { fprintf(stderr, "nav needs <app_id> and report|exit\n"); return 1; }
|
||||||
|
|||||||
@@ -51,6 +51,8 @@ enum {
|
|||||||
MSG_NOTIFY_QUERY = 0x0025, /* ↑ 查询内存活跃通知集(MeowPanel 实时化前置) */
|
MSG_NOTIFY_QUERY = 0x0025, /* ↑ 查询内存活跃通知集(MeowPanel 实时化前置) */
|
||||||
MSG_NOTIFY_DISMISS_ALL = 0x0026,/* ↑ 一键清除(保留 ongoing/media/progress 进行中,同通知中心 UI) */
|
MSG_NOTIFY_DISMISS_ALL = 0x0026,/* ↑ 一键清除(保留 ongoing/media/progress 进行中,同通知中心 UI) */
|
||||||
MSG_NOTIFY_QUERY_RESULT = 0x0027, /* ↓ 活跃通知集序列化文本(JSON,字段同 TF 归档) */
|
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_DECLARE = 0x0030, /* ↑ (重)声明路由表 */
|
||||||
MSG_ACT_QUERY = 0x0031, /* ↑ 解析 activity 链路 */
|
MSG_ACT_QUERY = 0x0031, /* ↑ 解析 activity 链路 */
|
||||||
MSG_ACT_RESULT = 0x0032, /* ↓ 返回链路 */
|
MSG_ACT_RESULT = 0x0032, /* ↓ 返回链路 */
|
||||||
@@ -243,6 +245,28 @@ typedef struct m_notify_query_result {
|
|||||||
char text[MEOW_IPC_MAX - 12]; /* 序列化 JSON 数组 */
|
char text[MEOW_IPC_MAX - 12]; /* 序列化 JSON 数组 */
|
||||||
} m_notify_query_result_t; /* 4096 B */
|
} 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)= */
|
/* ================= 专项服务 IPC(SYSTEM_FRAMEWORK_SERVICES §2.2/§2.3)= */
|
||||||
|
|
||||||
/* ---- MSG_PERF_QUERY 子集位(0=全部)---- */
|
/* ---- MSG_PERF_QUERY 子集位(0=全部)---- */
|
||||||
|
|||||||
@@ -51,6 +51,9 @@ extern "C" {
|
|||||||
#define WATCHDOG2 "/usr/bin/meow_watchdog"
|
#define WATCHDOG2 "/usr/bin/meow_watchdog"
|
||||||
#define POWMENU_MARKER "/tmp/meow_power_menu"
|
#define POWMENU_MARKER "/tmp/meow_power_menu"
|
||||||
#define POWMENU_ACTIVE "/tmp/meow_power_menu_active"
|
#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 "/sys/class/backlight/backlight_pwm0/brightness"
|
||||||
#define BACKLIGHT_POWER "/sys/class/backlight/backlight_pwm0/bl_power"
|
#define BACKLIGHT_POWER "/sys/class/backlight/backlight_pwm0/bl_power"
|
||||||
#define FB_DEV "/dev/fb0"
|
#define FB_DEV "/dev/fb0"
|
||||||
@@ -559,6 +562,10 @@ extern int meow_screen_on;
|
|||||||
extern int mfont_forced;
|
extern int mfont_forced;
|
||||||
extern int mfont_ready;
|
extern int mfont_ready;
|
||||||
extern int player_stopped;
|
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 const int pm_cx[PM_NUM];
|
||||||
extern int power_forceoff_ms;
|
extern int power_forceoff_ms;
|
||||||
extern int power_longpress_menu_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);
|
void meow_font_scan_presets_root(const char *card_root);
|
||||||
int meow_ipc_init(void);
|
int meow_ipc_init(void);
|
||||||
void meow_ipc_process(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_off(void);
|
||||||
void meow_panel_on(void);
|
void meow_panel_on(void);
|
||||||
void meow_parse_app_json(FILE *f, struct app *a);
|
void meow_parse_app_json(FILE *f, struct app *a);
|
||||||
@@ -717,6 +729,9 @@ int show_power_menu(void);
|
|||||||
int stop_player(void);
|
int stop_player(void);
|
||||||
/* 电源管理服务 IPC(SYSTEM_FRAMEWORK_SERVICES §3.2:launcher 电源动作经 meow_svc_power 执行) */
|
/* 电源管理服务 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);
|
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);
|
int meow_power_action(uint32_t cmd);
|
||||||
void ui_draw(int x, int y, const char *s, uint32_t color, int size);
|
void ui_draw(int x, int y, const char *s, uint32_t color, int size);
|
||||||
int ui_width(const char *s, int size);
|
int ui_width(const char *s, int size);
|
||||||
|
|||||||
Binary file not shown.
@@ -11,6 +11,12 @@ static struct app *g_ncp_apps = NULL; /* run_notif_center 注入(通知点
|
|||||||
|
|
||||||
static int g_ncp_napps = 0;
|
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 读/写,零旁路)。
|
/* ⑥ 勿扰面板内开关:当前 dnd 态(开面板/切换时从 meow_settings notify.dnd 读/写,零旁路)。
|
||||||
* ncp_collect_cb / ncp_active_count 按它过滤行(only urgency>=NCP_DND_MIN_URGENCY when on)。 */
|
* 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 (;;) {
|
for (;;) {
|
||||||
unsigned long long now = now_ms();
|
unsigned long long now = now_ms();
|
||||||
if (sigterm_flag) break;
|
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);
|
int animating = anim_ok && (anim_p != anim_target);
|
||||||
|
|
||||||
/* 自动息屏(同设置页语义) */
|
/* 自动息屏(同设置页语义) */
|
||||||
|
|||||||
@@ -829,6 +829,28 @@ static pthread_t ipc_pthread;
|
|||||||
|
|
||||||
static struct meow_ipc_conn ipc_conns[MEOW_IPC_CONNS];
|
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 视为失败。 */
|
/* 写一帧(16B 头 + 负载)。返回写入字节;<=0 视为失败。 */
|
||||||
static size_t ipc_send(int fd, uint16_t type, uint32_t req_id,
|
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);
|
logmsg("ipc: NOTIFY_DISMISS_ALL -> %d cleared", rc);
|
||||||
break;
|
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:
|
case MSG_NOTIFY_LIST:
|
||||||
/* ↓ 守护→App 下发通知清单(重同步)。本帧不来自客户端(无请求类型),故不在此应答;
|
/* ↓ 守护→App 下发通知清单(重同步)。本帧不来自客户端(无请求类型),故不在此应答;
|
||||||
* 需用时由守护主动经 meow_notif_serialize_list() + ipc_send 下发(M5 通知中心接入)。 */
|
* 需用时由守护主动经 meow_notif_serialize_list() + ipc_send 下发(M5 通知中心接入)。 */
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ AR="${AR:-/usr/bin/ar}" # 系统 GNU ar 即可归档 mipsel ELF 对象(架
|
|||||||
rm -f meow_ui.a *.o
|
rm -f meow_ui.a *.o
|
||||||
|
|
||||||
# -I../meow_launcherd:meow_notify_ipc.c 复用共享协议头 meow_ipc.h(同一份、杜绝两端漂移)
|
# -I../meow_launcherd:meow_notify_ipc.c 复用共享协议头 meow_ipc.h(同一份、杜绝两端漂移)
|
||||||
OBJS="meow_ui_gfx meow_ui_log meow_ui_notify meow_notify_ipc meow_ui_img meow_ui_widget meow_ui_input meow_ui_theme meow_ui_ime meow_ui_sys meow_ui_vol meow_ui_activity meow_ui_global meow_ui_dialog meow_ui_config meow_ui_draw meow_ui_view stb_image"
|
OBJS="meow_ui_gfx meow_ui_log meow_ui_notify meow_notify_ipc meow_ui_img meow_ui_widget meow_ui_input meow_ui_theme meow_ui_ime meow_ui_sys meow_ui_vol meow_ui_activity meow_ui_global meow_ui_dialog meow_ui_config meow_ui_draw meow_ui_view meow_ui_nav meow_ui_media stb_image"
|
||||||
CFLAGS="-Os -static -I$ROOT/patch_v2/meow_launcherd"
|
CFLAGS="-Os -static -I$ROOT/patch_v2/meow_launcherd"
|
||||||
for f in $OBJS; do
|
for f in $OBJS; do
|
||||||
echo "== cc $f =="
|
echo "== cc $f =="
|
||||||
|
|||||||
@@ -280,3 +280,55 @@ void meow_notify_ipc_update_progress(uint32_t nid, int progress)
|
|||||||
}
|
}
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---- 通用一次性帧(meow_ui_nav / notif_open 复用本传输层) ---- */
|
||||||
|
int meow_notify_ipc_send_raw(uint16_t type, const void *payload, uint32_t len, int want_ack)
|
||||||
|
{
|
||||||
|
int fd = conn_path(ipc_sock_path());
|
||||||
|
if (fd < 0) return -1;
|
||||||
|
set_ack_timeout(fd);
|
||||||
|
if (!send_frame(fd, type, 0, payload, len)) { close(fd); return -1; }
|
||||||
|
if (want_ack) {
|
||||||
|
/* 阻塞读一帧应答头(best-effort:读到即确认 launcher 已处理;内容不关心)。
|
||||||
|
* 超时(SO_RCVTIMEO)/断开 → read_all 返回 0,视为已发出(不崩)。 */
|
||||||
|
uint8_t rhdr[MEOW_IPC_HDR_LEN];
|
||||||
|
if (read_all(fd, rhdr, sizeof rhdr)) {
|
||||||
|
/* 读完应答负载(若声明),保持连接语义一致;不校验 magic(best-effort) */
|
||||||
|
uint32_t rlen;
|
||||||
|
memcpy(&rlen, rhdr + 12, 4);
|
||||||
|
if (rlen > 0 && rlen <= MEOW_IPC_MAX) {
|
||||||
|
uint8_t pay[256];
|
||||||
|
size_t to = rlen < sizeof pay ? rlen : sizeof pay;
|
||||||
|
(void)read_all(fd, pay, to);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void meow_notify_ipc_notif_open(const char *app_id, uint32_t event_seq,
|
||||||
|
uint16_t origin_x, uint16_t origin_y,
|
||||||
|
uint16_t hotzone, uint8_t flags)
|
||||||
|
{
|
||||||
|
m_notif_open_t o;
|
||||||
|
memset(&o, 0, sizeof o);
|
||||||
|
snprintf(o.app_id, sizeof o.app_id, "%s",
|
||||||
|
(app_id && app_id[0]) ? app_id : meow_notify_ipc_app_id());
|
||||||
|
o.event_seq = event_seq;
|
||||||
|
o.origin_x = origin_x;
|
||||||
|
o.origin_y = origin_y;
|
||||||
|
o.hotzone = hotzone;
|
||||||
|
o.flags = flags;
|
||||||
|
(void)meow_notify_ipc_send_raw(MSG_NOTIF_OPEN, &o, (uint32_t)sizeof o, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void meow_notify_ipc_notif_close(const char *app_id, uint32_t reason)
|
||||||
|
{
|
||||||
|
m_notif_close_t o;
|
||||||
|
memset(&o, 0, sizeof o);
|
||||||
|
snprintf(o.app_id, sizeof o.app_id, "%s",
|
||||||
|
(app_id && app_id[0]) ? app_id : meow_notify_ipc_app_id());
|
||||||
|
o.reason = reason;
|
||||||
|
(void)meow_notify_ipc_send_raw(MSG_NOTIF_CLOSE, &o, (uint32_t)sizeof o, 1);
|
||||||
|
}
|
||||||
@@ -43,6 +43,20 @@ void meow_notify_ipc_dismiss(uint32_t nid);
|
|||||||
* 不落盘)。best-effort:写失败/无 socket 静默。 */
|
* 不落盘)。best-effort:写失败/无 socket 静默。 */
|
||||||
void meow_notify_ipc_update_progress(uint32_t nid, int progress);
|
void meow_notify_ipc_update_progress(uint32_t nid, int progress);
|
||||||
|
|
||||||
|
/* 通用一次性请求:发 type+payload 帧,want_ack 时阻塞读一帧应答(忽略其内容,仅确保
|
||||||
|
* launcher 已处理完再关连接,防「写后即关」丢帧竞态)。返回 0=已发出(want_ack 含读到应答);
|
||||||
|
* -1=无 socket/写失败/读失败。best-effort,供 SDK 内部 nav / notif_open 复用。 */
|
||||||
|
int meow_notify_ipc_send_raw(uint16_t type, const void *payload, uint32_t len, int want_ack);
|
||||||
|
|
||||||
|
/* 一次性发送 MSG_NOTIF_OPEN(App 前台顶部下滑 → 请求 launcher 展开通知中心叠加会话,
|
||||||
|
* FRAMEWORK_DESIGN §10.5)。App 只检测+上报、不渲染系统 UI。best-effort:失败静默。 */
|
||||||
|
void meow_notify_ipc_notif_open(const char *app_id, uint32_t event_seq,
|
||||||
|
uint16_t origin_x, uint16_t origin_y,
|
||||||
|
uint16_t hotzone, uint8_t flags);
|
||||||
|
|
||||||
|
/* 一次性发送 MSG_NOTIF_CLOSE(App 告知结束/退出 → launcher 收尾兜底,可选用途)。best-effort。 */
|
||||||
|
void meow_notify_ipc_notif_close(const char *app_id, uint32_t reason);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -218,6 +218,7 @@ int meow_ui_long_press_ms(void); /* 当前阈值(默认
|
|||||||
void meow_ui_activity_push(int id);
|
void meow_ui_activity_push(int id);
|
||||||
void meow_ui_activity_pop(void);
|
void meow_ui_activity_pop(void);
|
||||||
int meow_ui_activity_active(void); /* 当前 active activity id(栈空=0) */
|
int meow_ui_activity_active(void); /* 当前 active activity id(栈空=0) */
|
||||||
|
int meow_ui_activity_depth(void); /* 当前栈深(push/pop 页数;供 M3 导航上报用) */
|
||||||
|
|
||||||
/* ② App 跨 Activity 控件(SDK 托管,骨架)。SDK 维护全局控件列表:
|
/* ② App 跨 Activity 控件(SDK 托管,骨架)。SDK 维护全局控件列表:
|
||||||
* - 渲染:每帧(有脏区需上屏时,meow_ui_gfx present())在 active activity 内容之上、
|
* - 渲染:每帧(有脏区需上屏时,meow_ui_gfx present())在 active activity 内容之上、
|
||||||
|
|||||||
@@ -31,3 +31,8 @@ int meow_ui_activity_active(void)
|
|||||||
{
|
{
|
||||||
return g_act_n > 0 ? g_act[g_act_n - 1] : 0;
|
return g_act_n > 0 ? g_act[g_act_n - 1] : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int meow_ui_activity_depth(void)
|
||||||
|
{
|
||||||
|
return g_act_n;
|
||||||
|
}
|
||||||
@@ -436,8 +436,9 @@ void meow_ui_run(const meow_ui_events *ev)
|
|||||||
} else if (e.type == MEOW_EV_NOTIF_PULL) {
|
} else if (e.type == MEOW_EV_NOTIF_PULL) {
|
||||||
if (in_startup) continue; /* 启动窗口顶区误触残留:丢弃,防进入即开通知中心 */
|
if (in_startup) continue; /* 启动窗口顶区误触残留:丢弃,防进入即开通知中心 */
|
||||||
/* 系统级「下拉展开通知中心」:起始于顶部热区的下滑为系统保留手势,不下发
|
/* 系统级「下拉展开通知中心」:起始于顶部热区的下滑为系统保留手势,不下发
|
||||||
* APP on_swipe;路由到系统钩子(当前为记录占位,通知中心 UI 为后续里程碑)。 */
|
* APP on_swipe;路由到系统钩子(App 进程只检测+经 IPC 上报 MSG_NOTIF_OPEN,
|
||||||
meow_ui_sys_notif_pull();
|
* 通知中心由 launcher 渲染,见 FRAMEWORK_DESIGN §10.5)。 */
|
||||||
|
meow_ui_sys_notif_pull(e.a); /* e.a = 手势按下原点 Y */
|
||||||
} else if (e.type == MEOW_EV_TOUCH_DOWN || e.type == MEOW_EV_TOUCH_MOVE) {
|
} else if (e.type == MEOW_EV_TOUCH_DOWN || e.type == MEOW_EV_TOUCH_MOVE) {
|
||||||
/* M2.71-bug2/6:拖动过程(跟手)。IME 弹出时触摸优先喂键盘(键盘覆盖层拦截,
|
/* M2.71-bug2/6:拖动过程(跟手)。IME 弹出时触摸优先喂键盘(键盘覆盖层拦截,
|
||||||
* on_touch_* 丢弃,避免按到键盘却在背后控件上锁拖拽);启动窗口也吞(防残留点按
|
* on_touch_* 丢弃,避免按到键盘却在背后控件上锁拖拽);启动窗口也吞(防残留点按
|
||||||
|
|||||||
@@ -0,0 +1,253 @@
|
|||||||
|
/* meow_ui_media.c — App 侧媒体会话封装(SDK 公共件;见 meow_ui_media.h)。
|
||||||
|
*
|
||||||
|
* 传输:AF_UNIX SOCK_STREAM 到 MEOW_IPC_SOCK(缺省 /usr/data/meow_ipc.sock)。后台线程持有
|
||||||
|
* 持久连接:HELLO(app_id, MF_PLAYBACK) 注册为媒体源 + poll 读 launcher 下发 MSG_PLAYCTL(经
|
||||||
|
* 该连接 fd 回发,见 launcher meow_media.c send_playctl)。meow_ui_media_meta() 在调用线程
|
||||||
|
* 直接经同一连接发 MSG_META(全双工:写 META、读 PLAYCTL 互不干扰)。
|
||||||
|
*
|
||||||
|
* 连接生命周期(best-effort):懒连(首次 meta / 注册回调时 connect+HELLO+ACK);
|
||||||
|
* 断线(读失败/写失败)→ close+置 -1,下次 meta 重连;launcher 不在 → connect 失败 → 静默。
|
||||||
|
* 线程安全:g_fd 读写经互斥;后台线程只在 poll/读失败时改 fd,meta 线程只在 connect 时改 fd。
|
||||||
|
*/
|
||||||
|
#include "meow_ui_media.h"
|
||||||
|
#include "meow_ipc.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
|
||||||
|
static pthread_mutex_t g_mu = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
static char g_app[MEOW_APP_ID_MAX] = "";
|
||||||
|
static int g_app_set = 0;
|
||||||
|
static int g_fd = -1;
|
||||||
|
static pthread_t g_thr;
|
||||||
|
static int g_thr_on = 0;
|
||||||
|
static volatile int g_quit = 0;
|
||||||
|
static meow_media_playctl_fn g_cb = NULL;
|
||||||
|
|
||||||
|
/* 后台线程主体(定义在文件后段;on_playctl 懒起时引用)。 */
|
||||||
|
static void *media_thread_main(void *arg);
|
||||||
|
|
||||||
|
static const char *sock_path(void)
|
||||||
|
{
|
||||||
|
const char *e = getenv("MEOW_IPC_SOCK");
|
||||||
|
return (e && e[0]) ? e : MEOW_IPC_PATH;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int connect_path(const char *path)
|
||||||
|
{
|
||||||
|
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if (fd < 0) return -1;
|
||||||
|
struct sockaddr_un a;
|
||||||
|
memset(&a, 0, sizeof a);
|
||||||
|
a.sun_family = AF_UNIX;
|
||||||
|
strncpy(a.sun_path, path, sizeof a.sun_path - 1);
|
||||||
|
if (connect(fd, (struct sockaddr *)&a, sizeof a) < 0) { close(fd); return -1; }
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 写满 n 字节(EINTR 重试);失败返回 0。 */
|
||||||
|
static int write_all(int fd, const uint8_t *p, size_t n)
|
||||||
|
{
|
||||||
|
size_t off = 0;
|
||||||
|
while (off < n) {
|
||||||
|
ssize_t w = write(fd, p + off, n - off);
|
||||||
|
if (w < 0) { if (errno == EINTR) continue; return 0; }
|
||||||
|
if (w == 0) return 0;
|
||||||
|
off += (size_t)w;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int read_all(int fd, uint8_t *p, size_t n)
|
||||||
|
{
|
||||||
|
size_t off = 0;
|
||||||
|
while (off < n) {
|
||||||
|
ssize_t r = read(fd, p + off, n - off);
|
||||||
|
if (r < 0) { if (errno == EINTR) continue; return 0; }
|
||||||
|
if (r == 0) return 0;
|
||||||
|
off += (size_t)r;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 组装并写出 16B(LE)帧头 + 负载。 */
|
||||||
|
static int send_frame(int fd, uint16_t type, uint32_t req_id,
|
||||||
|
const void *payload, uint32_t len)
|
||||||
|
{
|
||||||
|
uint8_t hdr[MEOW_IPC_HDR_LEN];
|
||||||
|
uint32_t magic = MEOW_IPC_MAGIC;
|
||||||
|
uint16_t ver = MEOW_IPC_VERSION;
|
||||||
|
memcpy(hdr + 0, &magic, 4);
|
||||||
|
memcpy(hdr + 4, &ver, 2);
|
||||||
|
memcpy(hdr + 6, &type, 2);
|
||||||
|
memcpy(hdr + 8, &req_id, 4);
|
||||||
|
memcpy(hdr + 12, &len, 4);
|
||||||
|
if (!write_all(fd, hdr, sizeof hdr)) return -1;
|
||||||
|
if (len && payload && !write_all(fd, payload, len)) return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 读一帧头+负载;返回负载长(≥0),-1=断/坏帧。 */
|
||||||
|
static int read_frame(int fd, uint16_t *type, uint8_t *rbuf, uint32_t rcap)
|
||||||
|
{
|
||||||
|
uint8_t hdr[MEOW_IPC_HDR_LEN];
|
||||||
|
if (!read_all(fd, hdr, sizeof hdr)) return -1;
|
||||||
|
uint32_t magic, len; uint16_t ver;
|
||||||
|
memcpy(&magic, hdr, 4); memcpy(&ver, hdr + 4, 2); memcpy(type, hdr + 6, 2);
|
||||||
|
memcpy(&len, hdr + 12, 4);
|
||||||
|
if (magic != MEOW_IPC_MAGIC || ver != MEOW_IPC_VERSION) return -1;
|
||||||
|
if (len > rcap) len = rcap;
|
||||||
|
if (len > 0 && !read_all(fd, rbuf, len)) return -1;
|
||||||
|
return (int)len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void media_app_id(void)
|
||||||
|
{
|
||||||
|
if (g_app_set) return;
|
||||||
|
const char *s = getenv("MEOW_LOG_APP");
|
||||||
|
snprintf(g_app, sizeof g_app, "%s", (s && s[0]) ? s : "app");
|
||||||
|
g_app_set = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (调用方已持 mu)连接 + HELLO(MF_PLAYBACK) + 读 ACK。成功存 g_fd 返回 fd;失败 -1。 */
|
||||||
|
static int conn_locked(void)
|
||||||
|
{
|
||||||
|
if (g_fd >= 0) return g_fd;
|
||||||
|
int fd = connect_path(sock_path());
|
||||||
|
if (fd < 0) return -1;
|
||||||
|
media_app_id();
|
||||||
|
m_hello_t h;
|
||||||
|
memset(&h, 0, sizeof h);
|
||||||
|
snprintf(h.app_id, sizeof h.app_id, "%s", g_app);
|
||||||
|
h.version = 1;
|
||||||
|
h.flags = MF_PLAYBACK;
|
||||||
|
if (send_frame(fd, MSG_HELLO, 1, &h, sizeof h) != 0) { close(fd); return -1; }
|
||||||
|
uint8_t rbuf[256];
|
||||||
|
uint16_t rt;
|
||||||
|
if (read_frame(fd, &rt, rbuf, sizeof rbuf) < 0 ||
|
||||||
|
(rt != MSG_ACK && rt != MSG_ERR)) { close(fd); return -1; }
|
||||||
|
g_fd = fd;
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (调用方已持 mu)标记断线并关闭。 */
|
||||||
|
static void mark_dead_locked(void)
|
||||||
|
{
|
||||||
|
if (g_fd >= 0) { close(g_fd); g_fd = -1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
void meow_ui_media_init(const char *app_id)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&g_mu);
|
||||||
|
if (app_id && app_id[0]) {
|
||||||
|
snprintf(g_app, sizeof g_app, "%s", app_id);
|
||||||
|
g_app_set = 1;
|
||||||
|
} else if (!g_app_set) {
|
||||||
|
const char *s = getenv("MEOW_LOG_APP");
|
||||||
|
snprintf(g_app, sizeof g_app, "%s", (s && s[0]) ? s : "app");
|
||||||
|
g_app_set = 1;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&g_mu);
|
||||||
|
}
|
||||||
|
|
||||||
|
void meow_ui_media_on_playctl(meow_media_playctl_fn cb)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&g_mu);
|
||||||
|
g_cb = cb;
|
||||||
|
int need_start = !g_thr_on;
|
||||||
|
if (need_start) { g_quit = 0; g_thr_on = 1; }
|
||||||
|
pthread_mutex_unlock(&g_mu);
|
||||||
|
if (need_start) pthread_create(&g_thr, NULL, media_thread_main, NULL);
|
||||||
|
/* detach:App 退出经 meow_ui_media_stop 设 g_quit 后线程自身退出,无需 join */
|
||||||
|
if (need_start) pthread_detach(g_thr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void meow_ui_media_meta(uint32_t state, uint32_t pos_ms, uint32_t dur_ms,
|
||||||
|
const char *title, const char *artist, const char *album,
|
||||||
|
const char *artwork, const char *lyric)
|
||||||
|
{
|
||||||
|
m_meta_t mm;
|
||||||
|
memset(&mm, 0, sizeof mm);
|
||||||
|
pthread_mutex_lock(&g_mu);
|
||||||
|
media_app_id();
|
||||||
|
snprintf(mm.app_id, sizeof mm.app_id, "%s", g_app);
|
||||||
|
mm.state = state;
|
||||||
|
mm.pos_ms = pos_ms;
|
||||||
|
mm.dur_ms = dur_ms;
|
||||||
|
if (title) snprintf(mm.title, sizeof mm.title, "%s", title);
|
||||||
|
if (artist) snprintf(mm.artist, sizeof mm.artist, "%s", artist);
|
||||||
|
if (album) snprintf(mm.album, sizeof mm.album, "%s", album);
|
||||||
|
if (artwork && artwork[0]) { snprintf(mm.artwork, sizeof mm.artwork, "%s", artwork); mm.flags |= MF_ART | MF_COVER; }
|
||||||
|
if (lyric && lyric[0]) { snprintf(mm.lyric, sizeof mm.lyric, "%s", lyric); mm.flags |= MF_LYRIC; }
|
||||||
|
|
||||||
|
int fd = conn_locked();
|
||||||
|
if (fd >= 0) {
|
||||||
|
if (send_frame(fd, MSG_META, (uint32_t)((uint64_t)pos_ms & 0x7FFFFFFF), &mm, sizeof mm) != 0) {
|
||||||
|
mark_dead_locked(); /* 断线:下次 meta 重连 */
|
||||||
|
fd = conn_locked();
|
||||||
|
if (fd >= 0) (void)send_frame(fd, MSG_META, (uint32_t)((uint64_t)pos_ms & 0x7FFFFFFF), &mm, sizeof mm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&g_mu);
|
||||||
|
}
|
||||||
|
|
||||||
|
void meow_ui_media_stop(void)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&g_mu);
|
||||||
|
g_quit = 1;
|
||||||
|
mark_dead_locked();
|
||||||
|
g_thr_on = 0;
|
||||||
|
pthread_mutex_unlock(&g_mu);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 后台线程主体:懒连 + poll 读 launcher 下发 MSG_PLAYCTL → 调 cb。 */
|
||||||
|
static void *media_thread_main(void *arg)
|
||||||
|
{
|
||||||
|
(void)arg;
|
||||||
|
while (!g_quit) {
|
||||||
|
pthread_mutex_lock(&g_mu);
|
||||||
|
int fd = g_fd;
|
||||||
|
if (fd < 0) fd = conn_locked(); /* 懒连/重连(best-effort;失败则回到循环再试) */
|
||||||
|
pthread_mutex_unlock(&g_mu);
|
||||||
|
if (fd < 0) { usleep(500000); continue; }
|
||||||
|
|
||||||
|
struct pollfd p;
|
||||||
|
p.fd = fd; p.events = POLLIN; p.revents = 0;
|
||||||
|
int pr = poll(&p, 1, 200);
|
||||||
|
if (pr < 0 && errno != EINTR) {
|
||||||
|
pthread_mutex_lock(&g_mu); mark_dead_locked(); pthread_mutex_unlock(&g_mu);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (pr > 0 && (p.revents & (POLLHUP | POLLERR | POLLNVAL))) {
|
||||||
|
pthread_mutex_lock(&g_mu); mark_dead_locked(); pthread_mutex_unlock(&g_mu);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (pr > 0 && (p.revents & POLLIN)) {
|
||||||
|
uint8_t rbuf[MEOW_IPC_MAX];
|
||||||
|
uint16_t type;
|
||||||
|
int n = read_frame(fd, &type, rbuf, sizeof rbuf);
|
||||||
|
if (n < 0) {
|
||||||
|
pthread_mutex_lock(&g_mu); mark_dead_locked(); pthread_mutex_unlock(&g_mu);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (type == MSG_PLAYCTL && n >= (int)sizeof(m_playctl_t)) {
|
||||||
|
m_playctl_t c;
|
||||||
|
memcpy(&c, rbuf, sizeof c);
|
||||||
|
/* g_cb 经互斥读快照后调用(App 回调可能在 UI 线程,简化为直接调) */
|
||||||
|
pthread_mutex_lock(&g_mu);
|
||||||
|
meow_media_playctl_fn cb = g_cb;
|
||||||
|
pthread_mutex_unlock(&g_mu);
|
||||||
|
if (cb) cb((int)c.cmd, (int64_t)c.seek_ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_mutex_lock(&g_mu); mark_dead_locked(); pthread_mutex_unlock(&g_mu);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/* meow_ui_media.h — App 侧媒体会话封装(SDK 公共件;FRAMEWORK_DESIGN 附录 B / D1)。
|
||||||
|
*
|
||||||
|
* 让框架/第三方播放类 App 以 SDK 标准上报实时元数据(MSG_META)并接收控制(MSG_PLAYCTL)。
|
||||||
|
* 声明 MF_PLAYBACK 的 App 首次上报 → launcher 注册为媒体源(最近活跃=当前源);通知中心
|
||||||
|
* 媒体卡片 / 主界面 Now Playing 页据此显示与控制。
|
||||||
|
*
|
||||||
|
* 连接管理(best-effort):懒连(首次 meta/注册回调时 connect)+ 断线重连(下次上报时重试);
|
||||||
|
* launcher 不在 → 优雅静默降级,绝不崩。帧格式/负载复用 launcher 共享 meow_ipc.h(同一份)。
|
||||||
|
*
|
||||||
|
* 命名:launcher 侧已占用 meow_media_*(meow_media.c),SDK 侧统一 meow_ui_media_* 前缀,
|
||||||
|
* 避免两态同符号在 launcher(链 meow_ui.a + meow_media.c)出现重复定义。
|
||||||
|
*/
|
||||||
|
#ifndef MEOW_UI_MEDIA_H
|
||||||
|
#define MEOW_UI_MEDIA_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* 播放控制回调:cmd 1=播放 2=暂停 3=上一首 4=下一首 5=seek(seek_ms 为毫秒)。 */
|
||||||
|
typedef void (*meow_media_playctl_fn)(int cmd, int64_t seek_ms);
|
||||||
|
|
||||||
|
/* 指定 app_id(NULL=env MEOW_LOG_APP,缺省 "app")。幂等,首次调用生效。 */
|
||||||
|
void meow_ui_media_init(const char *app_id);
|
||||||
|
|
||||||
|
/* 注册播放控制回调并启动后台接收线程(懒起;重复调用幂等)。
|
||||||
|
* 线程:连接(HELLO+MF_PLAYBACK 注册为媒体源)→ poll 读 launcher 下发 MSG_PLAYCTL → 调 cb。 */
|
||||||
|
void meow_ui_media_on_playctl(meow_media_playctl_fn cb);
|
||||||
|
|
||||||
|
/* 上报实时元数据/进度/状态(state:0=停止 1=播放 2=暂停 3=缓冲)。
|
||||||
|
* artwork/lyric 非空 → 自动置 MF_ART/MF_COVER/MF_LYRIC 标志;封面/歌词路径或 MeowOS 槽位。
|
||||||
|
* best-effort:无 socket/launcher 不在 → 静默降级(打日志不崩)。 */
|
||||||
|
void meow_ui_media_meta(uint32_t state, uint32_t pos_ms, uint32_t dur_ms,
|
||||||
|
const char *title, const char *artist, const char *album,
|
||||||
|
const char *artwork, const char *lyric);
|
||||||
|
|
||||||
|
/* 停止后台接收线程并断开连接(App 退出时调;幂等)。 */
|
||||||
|
void meow_ui_media_stop(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif /* MEOW_UI_MEDIA_H */
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
/* meow_ui_nav.c — App 侧 M3 导航上报封装(SDK 公共件;见 meow_ui_nav.h)。
|
||||||
|
*
|
||||||
|
* 只做「组装 m_nav_t + 经 meow_notify_ipc 一次性发送 MSG_NAV」,不维护连接/订阅循环;
|
||||||
|
* launcher 主循环单写者消费(回复 ACK)。全部失败路径优雅降级(无 socket/写失败/超时
|
||||||
|
* → 静默返回,不崩)。状态(栈深/顶层/app_id)仅进程内缓存,无并发写(App 页面线程调用)。
|
||||||
|
*/
|
||||||
|
#include "meow_ui_nav.h"
|
||||||
|
#include "meow_ipc.h"
|
||||||
|
#include "meow_notify_ipc.h" /* meow_notify_ipc_send_raw(复用其一次性帧传输层) */
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
static char g_app_id[MEOW_APP_ID_MAX] = "";
|
||||||
|
static int g_depth = 0;
|
||||||
|
static char g_top[MEOW_ACT_MAX] = "";
|
||||||
|
static int g_inited = 0;
|
||||||
|
|
||||||
|
/* app_id:env MEOW_LOG_APP(与 meow_ui_log/meow_notify_ipc 取值口径一致),缺省 "app"。 */
|
||||||
|
static const char *nav_app_id(void)
|
||||||
|
{
|
||||||
|
if (!g_app_id[0]) {
|
||||||
|
const char *s = getenv("MEOW_LOG_APP");
|
||||||
|
snprintf(g_app_id, sizeof g_app_id, "%s", (s && s[0]) ? s : "app");
|
||||||
|
}
|
||||||
|
return g_app_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
int meow_ui_nav_init(void)
|
||||||
|
{
|
||||||
|
if (g_inited) return g_depth;
|
||||||
|
g_inited = 1;
|
||||||
|
nav_app_id();
|
||||||
|
const char *chain = getenv("MEOW_CHAIN");
|
||||||
|
if (chain && chain[0]) {
|
||||||
|
g_depth = 1;
|
||||||
|
for (const char *p = chain; *p; p++) if (*p == ';') g_depth++;
|
||||||
|
}
|
||||||
|
const char *act = getenv("MEOW_ACTIVITY");
|
||||||
|
if (act && act[0]) snprintf(g_top, sizeof g_top, "%s", act);
|
||||||
|
return g_depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
int meow_ui_nav_depth(void) { meow_ui_nav_init(); return g_depth; }
|
||||||
|
const char *meow_ui_nav_top(void) { meow_ui_nav_init(); return g_top; }
|
||||||
|
|
||||||
|
void meow_ui_nav_report(uint32_t depth, const char *top)
|
||||||
|
{
|
||||||
|
meow_ui_nav_init();
|
||||||
|
m_nav_t n;
|
||||||
|
memset(&n, 0, sizeof n);
|
||||||
|
snprintf(n.app_id, sizeof n.app_id, "%s", nav_app_id());
|
||||||
|
n.nav_type = MEOW_NAV_REPORT;
|
||||||
|
n.depth = depth;
|
||||||
|
if (top) snprintf(n.current, sizeof n.current, "%s", top);
|
||||||
|
(void)meow_notify_ipc_send_raw(MSG_NAV, &n, (uint32_t)sizeof n, 1);
|
||||||
|
/* 进程内镜像:App 侧也维持当前栈深/顶层(跨 TU 只读用) */
|
||||||
|
g_depth = (int)depth;
|
||||||
|
if (top) snprintf(g_top, sizeof g_top, "%s", top);
|
||||||
|
}
|
||||||
|
|
||||||
|
void meow_ui_nav_request_exit(void)
|
||||||
|
{
|
||||||
|
meow_ui_nav_init();
|
||||||
|
m_nav_t n;
|
||||||
|
memset(&n, 0, sizeof n);
|
||||||
|
snprintf(n.app_id, sizeof n.app_id, "%s", nav_app_id());
|
||||||
|
n.nav_type = MEOW_NAV_EXIT;
|
||||||
|
n.depth = 0;
|
||||||
|
(void)meow_notify_ipc_send_raw(MSG_NAV, &n, (uint32_t)sizeof n, 1);
|
||||||
|
g_depth = 0;
|
||||||
|
g_top[0] = 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
/* meow_ui_nav.h — App 侧 M3 导航上报封装(SDK 公共件)。
|
||||||
|
*
|
||||||
|
* App 用它把「应用内页面栈深/顶层 activity」经 IPC 报给 launcher(MSG_NAV REPORT),
|
||||||
|
* 以及在根屏按返回时请求退出(MSG_NAV EXIT);launcher 据此维护每 app 导航栈(纯上报,
|
||||||
|
* 回退边界由 launcher 统一决定)。App 不渲染系统 UI(FRAMEWORK_DESIGN §3 / MEOW_APP_SDK §6.1)。
|
||||||
|
*
|
||||||
|
* 命名:launcher 侧已占用 meow_nav_*(app_launcher.c),SDK 侧统一 meow_ui_nav_* 前缀,
|
||||||
|
* 避免两态同符号在 launcher(链 meow_ui.a + app_launcher.c)出现重复定义。
|
||||||
|
*
|
||||||
|
* 冷启动链路:launcher 经 MEOW_ACTIVITY=<top> / MEOW_CHAIN=<id0>;<id1>;...;<top> 注入;
|
||||||
|
* meow_ui_nav_init() 读之初始化栈深/顶层。App 自行 push/pop 页面后经 meow_ui_nav_report()
|
||||||
|
* 上报新深度/顶层。
|
||||||
|
*/
|
||||||
|
#ifndef MEOW_UI_NAV_H
|
||||||
|
#define MEOW_UI_NAV_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* 从 env 初始化(幂等):MEOW_CHAIN 段数→栈深;MEOW_ACTIVITY→顶层 activity_id。
|
||||||
|
* 未注入(裸启动)→ 深度 0、顶层空串。app_id 取 env MEOW_LOG_APP(缺省 "app")。
|
||||||
|
* 返回当前逻辑深度(0=未注入/裸启动)。 */
|
||||||
|
int meow_ui_nav_init(void);
|
||||||
|
|
||||||
|
/* 当前栈深 / 顶层 activity_id(meow_ui_nav_init 后为其初值;App 上报后更新)。 */
|
||||||
|
int meow_ui_nav_depth(void);
|
||||||
|
const char *meow_ui_nav_top(void);
|
||||||
|
|
||||||
|
/* 上报 MEOW_NAV_REPORT:App 弹/推自己的屏后调用(depth=当前逻辑深度 1..16,top=顶层)。
|
||||||
|
* best-effort:launcher 不在/无 socket 静默降级,不崩。 */
|
||||||
|
void meow_ui_nav_report(uint32_t depth, const char *top);
|
||||||
|
|
||||||
|
/* 上报 MEOW_NAV_EXIT:App 在根屏按返回,请求 launcher 终止应用回主屏。best-effort。 */
|
||||||
|
void meow_ui_nav_request_exit(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif /* MEOW_UI_NAV_H */
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
#include "meow_ui_sys.h"
|
#include "meow_ui_sys.h"
|
||||||
#include "meow_ui_internal.h"
|
#include "meow_ui_internal.h"
|
||||||
#include "meow_ui_vol.h"
|
#include "meow_ui_vol.h"
|
||||||
|
#include "meow_notify_ipc.h" /* meow_notify_ipc_notif_open:App 前台 #10.5 上报 */
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -256,12 +257,18 @@ int meow_ui_sys_statusbar_height(void)
|
|||||||
return meow_ui_statusbar_collapsed() ? MUI_SBAR_COLLAPSE_H : MUI_SBAR_H;
|
return meow_ui_statusbar_collapsed() ? MUI_SBAR_COLLAPSE_H : MUI_SBAR_H;
|
||||||
}
|
}
|
||||||
|
|
||||||
void meow_ui_sys_notif_pull(void)
|
void meow_ui_sys_notif_pull(int origin_y)
|
||||||
{
|
{
|
||||||
/* 系统级「下拉展开通知中心」手势接线(M2.5x):输入层识别出「起始于顶部热区的
|
/* 系统级「下拉展开通知中心」手势接线(FRAMEWORK_DESIGN §10.5):输入层识别出「起始于
|
||||||
* SWIPE_DOWN」后路由到此。通知中心 UI 为后续里程碑——此处先落记录(避免哑无痕);
|
* 顶部热区的 SWIPE_DOWN」后路由到此。App 进程只做检测+上报(不发事件给 on_swipe),
|
||||||
* 届时由对应代理在该入口挂真实展开逻辑(IPC SHOW_ACTIVITY / 覆盖层渲染)。 */
|
* 通知中心仍由 launcher 渲染——此处经 IPC 发 MSG_NOTIF_OPEN 请求 launcher 展开叠加会话
|
||||||
meow_log(MEOW_LOG_WARN, "notif_pull: notification center TBD (wired)");
|
* (SIGSTOP App + 读回其帧 + 叠加渲染通知中心)。best-effort:无 socket/launcher 不在
|
||||||
|
* → meow_notify_ipc_notif_open 静默降级。 */
|
||||||
|
static uint32_t seq = 0;
|
||||||
|
if (origin_y < 0) origin_y = 0;
|
||||||
|
meow_notify_ipc_notif_open(NULL, ++seq, 0, (uint16_t)origin_y,
|
||||||
|
(uint16_t)meow_ui_statusbar_hotzone(),
|
||||||
|
(uint8_t)(g_fullscreen ? 1 : 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
int meow_ui_sys_statusbar_force(void)
|
int meow_ui_sys_statusbar_force(void)
|
||||||
|
|||||||
@@ -64,10 +64,11 @@ int meow_ui_sys_statusbar_active(void);
|
|||||||
* MUI_SBAR_COLLAPSE_H(非满 28,app 内容可上移);否则 0(供 meow_ui_statusbar_height 用)。 */
|
* MUI_SBAR_COLLAPSE_H(非满 28,app 内容可上移);否则 0(供 meow_ui_statusbar_height 用)。 */
|
||||||
int meow_ui_sys_statusbar_height(void);
|
int meow_ui_sys_statusbar_height(void);
|
||||||
|
|
||||||
/* 系统级「下拉展开通知中心」手势挂钩(M2.5x 接线):输入层识别出「起始于顶部热区的
|
/* 系统级「下拉展开通知中心」手势挂钩(§10.5):输入层识别出「起始于顶部热区的 SWIPE_DOWN」
|
||||||
* SWIPE_DOWN」后路由到这里。通知中心 UI 为后续里程碑,此入口作记录/占位;届时由对应
|
* 后路由到这里(origin_y=手势按下原点 Y,供 launcher 复核是否在顶部热区)。App 进程只做
|
||||||
* 代理在此挂真实展开逻辑(IPC SHOW_ACTIVITY / 覆盖层渲染)。 */
|
* 检测+上报:经 IPC 发 MSG_NOTIF_OPEN 请求 launcher 展开通知中心叠加会话;**App 不渲染系统 UI**。
|
||||||
void meow_ui_sys_notif_pull(void);
|
* best-effort:无 socket/launcher 不在 → 静默降级。 */
|
||||||
|
void meow_ui_sys_notif_pull(int origin_y);
|
||||||
|
|
||||||
/* 自上次渲染后状态栏数据是否已变(1s 轻刷/显隐切换置位);读取即清除。
|
/* 自上次渲染后状态栏数据是否已变(1s 轻刷/显隐切换置位);读取即清除。
|
||||||
* 渲染后端 present() 用它决定是否重画顶部条(配合脏区/整帧判断)。 */
|
* 渲染后端 present() 用它决定是否重画顶部条(配合脏区/整帧判断)。 */
|
||||||
|
|||||||
Binary file not shown.
@@ -8,7 +8,7 @@
|
|||||||
* gcc -Wall -Wextra -o test_ime_fix test_ime_fix.c test_host_stub.c \
|
* gcc -Wall -Wextra -o test_ime_fix test_ime_fix.c test_host_stub.c \
|
||||||
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
|
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
|
||||||
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c \
|
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c \
|
||||||
* meow_ui_draw.c meow_ui_view.c meow_ui_img.c stb_image.c \
|
* meow_ui_draw.c meow_ui_view.c meow_ui_img.c meow_notify_ipc.c stb_image.c \
|
||||||
* -I. -I../meow_launcherd -DMEOW_UI_HOST_TEST -pthread -lm
|
* -I. -I../meow_launcherd -DMEOW_UI_HOST_TEST -pthread -lm
|
||||||
* (meow_ui_widget.c 新 W_LISTVIEW 引用 meow_ui_img_scaled → 需链 meow_ui_img.c+stb_image.c+-lm)
|
* (meow_ui_widget.c 新 W_LISTVIEW 引用 meow_ui_img_scaled → 需链 meow_ui_img.c+stb_image.c+-lm)
|
||||||
*/
|
*/
|
||||||
|
|||||||
Binary file not shown.
@@ -5,7 +5,7 @@
|
|||||||
* gcc -o test_meow_ui_widgets test_meow_ui_widgets.c test_host_stub.c \
|
* gcc -o test_meow_ui_widgets test_meow_ui_widgets.c test_host_stub.c \
|
||||||
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
|
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
|
||||||
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c \
|
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c \
|
||||||
* meow_ui_draw.c meow_ui_view.c meow_ui_img.c stb_image.c \
|
* meow_ui_draw.c meow_ui_view.c meow_ui_img.c meow_notify_ipc.c stb_image.c \
|
||||||
* -I. -I../meow_launcherd -DMEOW_UI_HOST_TEST -pthread -lm
|
* -I. -I../meow_launcherd -DMEOW_UI_HOST_TEST -pthread -lm
|
||||||
* 运行:MEOW_UI_DUMP=/tmp/b2.ppm ./test_meow_ui_widgets (可选,dump 屏幕)
|
* 运行:MEOW_UI_DUMP=/tmp/b2.ppm ./test_meow_ui_widgets (可选,dump 屏幕)
|
||||||
*
|
*
|
||||||
|
|||||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,191 @@
|
|||||||
|
/* test_notif_open.c — 宿主验证 §10.5「App 前台划出通知中心」的 SDK 侧上报链路:
|
||||||
|
* 1. m_notif_open_t / m_notif_close_t 帧组装解析(round-trip)。
|
||||||
|
* 2. meow_notify_ipc_notif_open/close 经真实 AF_UNIX socket 发到 mock launcher,
|
||||||
|
* 服务端收到 MSG_NOTIF_OPEN / MSG_NOTIF_CLOSE 且字段正确。
|
||||||
|
* 3. meow_ui_sys_notif_pull(origin_y)(顶部下滑系统手势钩子)→ 经 IPC 发 MSG_NOTIF_OPEN,
|
||||||
|
* 服务端收到(origin_y/hotzone 由 SDK 填)。不渲染系统 UI——纯上报。
|
||||||
|
* 4. meow_notify_ipc_send_raw 的 want_ack 路径(服务端回 ACK,不阻塞)。
|
||||||
|
*
|
||||||
|
* 编译(宿主 gcc,链 meow_notify_ipc + meow_ui_sys + gfx/theme/vol/log + 打桩 font/global):
|
||||||
|
* gcc -Wall -o test_notif_open test_notif_open.c test_host_stub.c \
|
||||||
|
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
|
||||||
|
* meow_notify_ipc.c \
|
||||||
|
* -I. -I../meow_launcherd -DMEOW_UI_HOST_TEST -pthread -lm
|
||||||
|
* 运行:./test_notif_open
|
||||||
|
*/
|
||||||
|
#include "meow_ui.h"
|
||||||
|
#include "meow_ui_sys.h" /* meow_ui_sys_notif_pull */
|
||||||
|
#include "meow_notify_ipc.h" /* meow_notify_ipc_notif_open/close + send_raw */
|
||||||
|
#include "meow_ipc.h" /* MSG_NOTIF_OPEN/CLOSE + m_notif_open_t/m_notif_close_t */
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#define TEST_SOCK "/tmp/meow_notif_open_test.sock"
|
||||||
|
#define ACK_CODE 0x4D00u /* 普通测试 ACK 载荷标记 */
|
||||||
|
|
||||||
|
static int g_srv_fd = -1;
|
||||||
|
static int g_stop = 0;
|
||||||
|
|
||||||
|
/* 服务端捕获到的帧(互斥保护) */
|
||||||
|
static pthread_mutex_t g_mu = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
static struct {
|
||||||
|
uint16_t type; /* 收到的帧 type */
|
||||||
|
uint16_t plen; /* payload 长 */
|
||||||
|
uint8_t pay[256];
|
||||||
|
} g_rx[8];
|
||||||
|
static int g_rx_n = 0;
|
||||||
|
|
||||||
|
static int srv_send(int fd, uint16_t type, uint8_t *pay, uint32_t len)
|
||||||
|
{
|
||||||
|
uint8_t hdr[MEOW_IPC_HDR_LEN];
|
||||||
|
uint32_t magic = MEOW_IPC_MAGIC; uint16_t ver = MEOW_IPC_VERSION;
|
||||||
|
uint32_t ack = ACK_CODE;
|
||||||
|
memcpy(hdr+0,&magic,4); memcpy(hdr+4,&ver,2); memcpy(hdr+6,&type,2); memcpy(hdr+8,&ack,4); memcpy(hdr+12,&len,4);
|
||||||
|
size_t off = 0;
|
||||||
|
while (off < sizeof hdr) { ssize_t w = write(fd, hdr+off, sizeof hdr-off); if (w<=0) return -1; off+=(size_t)w; }
|
||||||
|
if (len && pay) { off=0; while (off<len){ ssize_t w=write(fd, pay+off, len-off); if(w<=0)return -1; off+=(size_t)w; } }
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *srv_thread(void *arg)
|
||||||
|
{
|
||||||
|
(void)arg;
|
||||||
|
int lfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if (lfd < 0) return NULL;
|
||||||
|
unlink(TEST_SOCK);
|
||||||
|
struct sockaddr_un a; memset(&a,0,sizeof a); a.sun_family=AF_UNIX; strncpy(a.sun_path,TEST_SOCK,sizeof a.sun_path-1);
|
||||||
|
if (bind(lfd,(struct sockaddr*)&a,sizeof a)<0){ perror("bind"); close(lfd); return NULL; }
|
||||||
|
chmod(TEST_SOCK,0666);
|
||||||
|
if (listen(lfd,4)<0){ perror("listen"); close(lfd); return NULL; }
|
||||||
|
fcntl(lfd, F_SETFL, fcntl(lfd,F_GETFL,0)|O_NONBLOCK); /* 非阻塞 accept:g_stop 时及时退出 */
|
||||||
|
g_srv_fd = lfd;
|
||||||
|
while (!g_stop) {
|
||||||
|
int c = accept(lfd, NULL, NULL);
|
||||||
|
if (c < 0) {
|
||||||
|
if (errno==EAGAIN || errno==EWOULDBLOCK) { usleep(20000); continue; }
|
||||||
|
if (errno==EINTR) continue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* 读帧(一连接收若干帧;本测试每连接一帧) */
|
||||||
|
uint8_t hdr[MEOW_IPC_HDR_LEN];
|
||||||
|
size_t off=0;
|
||||||
|
while (off<sizeof hdr){ ssize_t r=read(c,hdr+off,sizeof hdr-off); if(r<=0) goto next; off+=(size_t)r; }
|
||||||
|
uint32_t magic,len; uint16_t ver,type;
|
||||||
|
memcpy(&magic,hdr,4); memcpy(&ver,hdr+4,2); memcpy(&type,hdr+6,2); memcpy(&len,hdr+12,4);
|
||||||
|
if (magic != MEOW_IPC_MAGIC || ver != MEOW_IPC_VERSION || len > 256) goto next;
|
||||||
|
pthread_mutex_lock(&g_mu);
|
||||||
|
if (g_rx_n < 8) {
|
||||||
|
uint8_t pay[256]; memset(pay,0,sizeof pay);
|
||||||
|
off=0; while(off<len){ ssize_t r=read(c,pay+off,len-off); if(r<=0)break; off+=(size_t)r; }
|
||||||
|
g_rx[g_rx_n].type=type; g_rx[g_rx_n].plen=(uint16_t)len; memcpy(g_rx[g_rx_n].pay,pay,len);
|
||||||
|
g_rx_n++;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&g_mu);
|
||||||
|
/* 回 ACK(m_ack_t,code=MEOW_OK, status=1);best-effort */
|
||||||
|
{
|
||||||
|
m_ack_t a; a.code=MEOW_OK; a.status=1;
|
||||||
|
(void)srv_send(c, MSG_ACK, (uint8_t*)&a, sizeof a);
|
||||||
|
}
|
||||||
|
next:
|
||||||
|
close(c);
|
||||||
|
}
|
||||||
|
close(lfd);
|
||||||
|
unlink(TEST_SOCK);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 取第 n 帧(n 从 0 起);返回 1=有,0=无。 */
|
||||||
|
static int rx_get(int n, uint16_t *type, uint16_t *plen, uint8_t *pay, int cap)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&g_mu);
|
||||||
|
int ok = (n < g_rx_n);
|
||||||
|
if (ok) { *type = g_rx[n].type; *plen = g_rx[n].plen;
|
||||||
|
size_t c = g_rx[n].plen < (uint16_t)cap ? g_rx[n].plen : (uint16_t)cap;
|
||||||
|
memcpy(pay, g_rx[n].pay, c); }
|
||||||
|
pthread_mutex_unlock(&g_mu);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
setenv("MEOW_IPC_SOCK", TEST_SOCK, 1);
|
||||||
|
setenv("MEOW_LOG_APP", "os.meow.hello", 1);
|
||||||
|
|
||||||
|
pthread_t th; pthread_create(&th,NULL,srv_thread,NULL);
|
||||||
|
/* 等服务端就绪 */
|
||||||
|
for (int i=0;i<50 && g_srv_fd<0;i++) usleep(20000);
|
||||||
|
if (g_srv_fd < 0) { fprintf(stderr,"server not ready\n"); return 1; }
|
||||||
|
|
||||||
|
int fail = 0;
|
||||||
|
|
||||||
|
/* 1) meow_notify_ipc_notif_open 发 MSG_NOTIF_OPEN(want_ack=1 → 服务端回 ACK,不阻塞) */
|
||||||
|
meow_notify_ipc_notif_open("os.meow.hello", 7, 3, 8, 28, 0);
|
||||||
|
usleep(100000);
|
||||||
|
uint16_t t, pl; uint8_t pay[256];
|
||||||
|
if (rx_get(0,&t,&pl,pay,sizeof pay) && t==MSG_NOTIF_OPEN && pl>=sizeof(m_notif_open_t)) {
|
||||||
|
m_notif_open_t o; memcpy(&o,pay,sizeof o);
|
||||||
|
if (strcmp(o.app_id,"os.meow.hello")==0 && o.event_seq==7 && o.origin_x==3 &&
|
||||||
|
o.origin_y==8 && o.hotzone==28 && o.flags==0) {
|
||||||
|
printf("PASS notif_open: type=0x%04x app=%s seq=%u oy=%u hz=%u\n", t, o.app_id, o.event_seq, o.origin_y, o.hotzone);
|
||||||
|
} else { printf("FAIL notif_open: field mismatch\n"); fail=1; }
|
||||||
|
} else { printf("FAIL notif_open: unexpected frame type=0x%04x plen=%u\n", t, pl); fail=1; }
|
||||||
|
|
||||||
|
/* 2) meow_ui_sys_notif_pull(5):顶部下滑系统手势 → SDK 上报 MSG_NOTIF_OPEN(不渲染系统 UI)。
|
||||||
|
* app_id 走 env MEOW_LOG_APP;hotzone=状态栏激活(28);fullscreen=0 → flags=0。 */
|
||||||
|
meow_ui_sys_notif_pull(5);
|
||||||
|
usleep(100000);
|
||||||
|
if (rx_get(1,&t,&pl,pay,sizeof pay) && t==MSG_NOTIF_OPEN && pl>=sizeof(m_notif_open_t)) {
|
||||||
|
m_notif_open_t o; memcpy(&o,pay,sizeof o);
|
||||||
|
if (strcmp(o.app_id,"os.meow.hello")==0 && o.origin_y==5 && o.hotzone==28 && o.flags==0) {
|
||||||
|
printf("PASS sys_notif_pull: gesture->MSG_NOTIF_OPEN app=%s oy=%u hz=%u nseq=%u\n", o.app_id, o.origin_y, o.hotzone, o.event_seq);
|
||||||
|
} else { printf("FAIL sys_notif_pull: field mismatch\n"); fail=1; }
|
||||||
|
} else { printf("FAIL sys_notif_pull: unexpected frame type=0x%04x plen=%u\n", t, pl); fail=1; }
|
||||||
|
|
||||||
|
/* 3) meow_notify_ipc_notif_close 发 MSG_NOTIF_CLOSE */
|
||||||
|
meow_notify_ipc_notif_close("os.meow.hello", 0);
|
||||||
|
usleep(100000);
|
||||||
|
if (rx_get(2,&t,&pl,pay,sizeof pay) && t==MSG_NOTIF_CLOSE && pl>=sizeof(m_notif_close_t)) {
|
||||||
|
m_notif_close_t o; memcpy(&o,pay,sizeof o);
|
||||||
|
if (strcmp(o.app_id,"os.meow.hello")==0 && o.reason==0) {
|
||||||
|
printf("PASS notif_close: type=0x%04x app=%s reason=%u\n", t, o.app_id, o.reason);
|
||||||
|
} else { printf("FAIL notif_close: field mismatch\n"); fail=1; }
|
||||||
|
} else { printf("FAIL notif_close: unexpected frame type=0x%04x plen=%u\n", t, pl); fail=1; }
|
||||||
|
|
||||||
|
/* 4) 帧组装解析 round-trip:构造 m_notif_open_t 序列化 → 解析恢复各字段。 */
|
||||||
|
{
|
||||||
|
m_notif_open_t a; memset(&a,0,sizeof a);
|
||||||
|
snprintf(a.app_id,sizeof a.app_id,"os.meow.hello");
|
||||||
|
a.event_seq=9; a.origin_x=2; a.origin_y=32; a.hotzone=4; a.flags=1;
|
||||||
|
m_notif_open_t b; memset(&b,0,sizeof b);
|
||||||
|
memcpy(&b,&a,sizeof a);
|
||||||
|
if (b.event_seq==9 && b.origin_y==32 && b.hotzone==4 && b.flags==1 && b.app_id[0]=='o')
|
||||||
|
printf("PASS roundtrip: m_notif_open_t 44B (sz=%zu)\n", sizeof a);
|
||||||
|
else { printf("FAIL roundtrip\n"); fail=1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 5) meow_notify_ipc_send_raw want_ack 路径(无效帧类型,服务端回 ERR? no——回 ACK;不崩即可) */
|
||||||
|
{
|
||||||
|
uint32_t x = 0x12345678u;
|
||||||
|
int r = meow_notify_ipc_send_raw(MSG_NOTIF_OPEN, &x, sizeof x, 1);
|
||||||
|
/* want_ack 读到 ACK 即返回 0;无服务端才 -1。这里服务端在跑,应为 0。 */
|
||||||
|
printf("%s send_raw(MSG_NOTIF_OPEN, want_ack) -> %d\n", r==0?"PASS":"FAIL", r);
|
||||||
|
if (r!=0) fail=1;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_stop = 1;
|
||||||
|
pthread_join(th, NULL); /* 服务端线程自关 lfd 并退出 */
|
||||||
|
|
||||||
|
if (fail) { printf("RESULT: FAIL\n"); return 1; }
|
||||||
|
printf("RESULT: ALL PASS\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
* test_host_stub.c meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
|
* test_host_stub.c meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
|
||||||
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c \
|
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c \
|
||||||
* meow_ui_img.c meow_ui_config.c meow_ui_notify.c meow_notify_ipc.c meow_ui_draw.c \
|
* meow_ui_img.c meow_ui_config.c meow_ui_notify.c meow_notify_ipc.c meow_ui_draw.c \
|
||||||
* meow_ui_view.c stb_image.c \
|
* meow_ui_view.c meow_ui_nav.c meow_ui_media.c stb_image.c \
|
||||||
* ../hello_app/ui/home/home.c ../hello_app/ui/full.c ../hello_app/ui/demo/demo.c \
|
* ../hello_app/ui/home/home.c ../hello_app/ui/full.c ../hello_app/ui/demo/demo.c \
|
||||||
* ../hello_app/ui/demo/list/list.c ../hello_app/ui/common/chrome.c \
|
* ../hello_app/ui/demo/list/list.c ../hello_app/ui/common/chrome.c \
|
||||||
* ../hello_app/logic/notify.c ../hello_app/logic/state.c \
|
* ../hello_app/logic/notify.c ../hello_app/logic/state.c \
|
||||||
|
|||||||
Executable
BIN
Binary file not shown.
@@ -7,10 +7,10 @@
|
|||||||
* collapse_offset() 随时间推进 → 28(收合出屏);set_collapsed(0) → offset 回 0(恢复)。
|
* collapse_offset() 随时间推进 → 28(收合出屏);set_collapsed(0) → offset 回 0(恢复)。
|
||||||
* 4. 收合 inset:collapsed → meow_ui_statusbar_height()==MUI_SBAR_COLLAPSE_H(4)。
|
* 4. 收合 inset:collapsed → meow_ui_statusbar_height()==MUI_SBAR_COLLAPSE_H(4)。
|
||||||
*
|
*
|
||||||
* 编译(宿主 gcc,链真实 gfx/sys/theme/vol/log + 打桩 font/global):
|
* 编译(宿主 gcc,链真实 gfx/sys/theme/vol/log + 打桩 font/global + meow_notify_ipc):
|
||||||
* gcc -o test_sb_collapse test_sb_collapse.c test_host_stub.c \
|
* gcc -o test_sb_collapse test_sb_collapse.c test_host_stub.c \
|
||||||
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
|
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c meow_notify_ipc.c \
|
||||||
* -I. -I../meow_launcherd -DMEOW_UI_HOST_TEST
|
* -I. -I../meow_launcherd -DMEOW_UI_HOST_TEST -pthread
|
||||||
*/
|
*/
|
||||||
#include "meow_ui.h"
|
#include "meow_ui.h"
|
||||||
#include "meow_ui_internal.h"
|
#include "meow_ui_internal.h"
|
||||||
|
|||||||
Executable
BIN
Binary file not shown.
@@ -10,7 +10,7 @@
|
|||||||
* gcc -Wall -o test_sdk_fixes test_sdk_fixes.c test_host_stub.c \
|
* gcc -Wall -o test_sdk_fixes test_sdk_fixes.c test_host_stub.c \
|
||||||
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
|
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
|
||||||
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c \
|
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c \
|
||||||
* meow_ui_draw.c meow_ui_view.c \
|
* meow_ui_draw.c meow_ui_view.c meow_notify_ipc.c \
|
||||||
* -I. -I../meow_launcherd -DMEOW_UI_HOST_TEST -pthread
|
* -I. -I../meow_launcherd -DMEOW_UI_HOST_TEST -pthread
|
||||||
*/
|
*/
|
||||||
#include "meow_ui.h"
|
#include "meow_ui.h"
|
||||||
|
|||||||
Reference in new issue
Block a user