meow_ui.a→9a8eb7ef; hello→fc299577; launcher→b33da20a; 宿主5套件+新test_notif_open+qemu分发 全过 出入: SDK符号用meow_ui_前缀防与launcher内同名冲突; 叠加会话真机待验; §10.5文档旧号段已过时
630 lines
25 KiB
C
630 lines
25 KiB
C
/* meow_ipc.c — MeowOS IPC 测试钩子(阶段 D · M0+M1;与 evtool 同定位)。
|
||
*
|
||
* 向 meow_launcherd 的 AF_UNIX IPC(FRAMEWORK_DESIGN §1)发帧、收应答并打印。
|
||
* 静态 MIPS32r2 musl(用 zig-mipsel-cc 编译),qemu-mipsel 亦可用。
|
||
*
|
||
* 子命令:
|
||
* meow_ipc hello <app_id> [version] [flags] → MSG_HELLO,期望 MSG_ACK
|
||
* meow_ipc ping [app_id] → hello 别名(默认 meow_ipc)
|
||
* meow_ipc query <app_id> <activity> → MSG_ACT_QUERY,期望 MSG_ACT_RESULT
|
||
* meow_ipc declare <app_id> <route_id=chain> [...] → MSG_ACT_DECLARE,期望 MSG_ACK
|
||
* 全局:
|
||
* --sock <path> 覆盖 socket 路径(默认可被 MEOW_IPC_SOCK 覆盖,再回退 /usr/data/meow_ipc.sock)
|
||
*/
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <stdint.h>
|
||
#include <unistd.h>
|
||
#include <fcntl.h>
|
||
#include <errno.h>
|
||
#include <sys/socket.h>
|
||
#include <sys/un.h>
|
||
#include <sys/stat.h>
|
||
#include "meow_ipc.h"
|
||
|
||
static const char *g_sock = NULL;
|
||
|
||
static void print_err(const m_err_t *e)
|
||
{
|
||
fprintf(stderr, "ERR 0x%x: %.*s\n", e->code, e->msg_len < 64 ? e->msg_len : 64, e->msg);
|
||
}
|
||
|
||
static int connect_path(const char *path)
|
||
{
|
||
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||
if (fd < 0) { perror("socket"); 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) {
|
||
fprintf(stderr, "connect %s: %s\n", path, strerror(errno));
|
||
close(fd);
|
||
return -1;
|
||
}
|
||
return fd;
|
||
}
|
||
|
||
/* 发一帧(头+负载),读回一帧(头+负载)。返回应答类型;-1=失败。 */
|
||
static int xchg(int fd, uint16_t type, uint32_t req_id, const void *payload, uint32_t len,
|
||
uint8_t *rbuf, uint32_t rcap)
|
||
{
|
||
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);
|
||
size_t off = 0;
|
||
while (off < sizeof hdr) {
|
||
ssize_t w = write(fd, hdr + off, sizeof hdr - off);
|
||
if (w <= 0) { perror("write hdr"); return -1; }
|
||
off += (size_t)w;
|
||
}
|
||
if (len && payload) {
|
||
size_t p = 0;
|
||
while (p < len) {
|
||
ssize_t w = write(fd, (const uint8_t *)payload + p, len - p);
|
||
if (w <= 0) { perror("write payload"); return -1; }
|
||
p += (size_t)w;
|
||
}
|
||
}
|
||
/* read response header */
|
||
uint8_t rhdr[MEOW_IPC_HDR_LEN];
|
||
off = 0;
|
||
while (off < sizeof rhdr) {
|
||
ssize_t r = read(fd, rhdr + off, sizeof rhdr - off);
|
||
if (r <= 0) { perror("read hdr"); return -1; }
|
||
off += (size_t)r;
|
||
}
|
||
uint32_t rmagic, rlen;
|
||
uint16_t rver, rtype;
|
||
memcpy(&rmagic, rhdr, 4);
|
||
memcpy(&rver, rhdr + 4, 2);
|
||
memcpy(&rtype, rhdr + 6, 2);
|
||
memcpy(&rlen, rhdr + 12, 4);
|
||
if (rmagic != MEOW_IPC_MAGIC) { fprintf(stderr, "bad resp magic %08x\n", rmagic); return -1; }
|
||
if (rver != MEOW_IPC_VERSION) { fprintf(stderr, "bad resp version %u\n", rver); return -1; }
|
||
if (rlen > rcap) rlen = rcap;
|
||
off = 0;
|
||
while (off < rlen) {
|
||
ssize_t r = read(fd, rbuf + off, rlen - off);
|
||
if (r <= 0) { perror("read payload"); return -1; }
|
||
off += (size_t)r;
|
||
}
|
||
return (int)rtype;
|
||
}
|
||
|
||
static int cmd_hello(const char *app_id, uint32_t version, uint32_t flags)
|
||
{
|
||
const char *sock = g_sock;
|
||
int fd = connect_path(sock);
|
||
if (fd < 0) return 1;
|
||
m_hello_t h;
|
||
memset(&h, 0, sizeof h);
|
||
strncpy(h.app_id, app_id, MEOW_APP_ID_MAX - 1);
|
||
h.version = version;
|
||
h.flags = flags;
|
||
uint8_t rbuf[256];
|
||
int rt = xchg(fd, MSG_HELLO, 1, &h, sizeof h, rbuf, sizeof rbuf);
|
||
close(fd);
|
||
if (rt == MSG_ACK) {
|
||
m_ack_t a;
|
||
memcpy(&a, rbuf, sizeof a);
|
||
printf("HELLO ok: code=%d status=%u (apps registered)\n", 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, "HELLO: unexpected resp type 0x%04x\n", rt);
|
||
return 1;
|
||
}
|
||
|
||
static int cmd_query(const char *app_id, const char *activity)
|
||
{
|
||
int fd = connect_path(g_sock);
|
||
if (fd < 0) return 1;
|
||
m_act_query_t q;
|
||
memset(&q, 0, sizeof q);
|
||
strncpy(q.app_id, app_id, MEOW_APP_ID_MAX - 1);
|
||
strncpy(q.activity, activity, MEOW_ACT_MAX - 1);
|
||
uint8_t rbuf[256];
|
||
int rt = xchg(fd, MSG_ACT_QUERY, 2, &q, sizeof q, rbuf, sizeof rbuf);
|
||
close(fd);
|
||
if (rt == MSG_ACT_RESULT) {
|
||
m_act_result_t r;
|
||
memcpy(&r, rbuf, sizeof r);
|
||
size_t cl = r.chain_len < MEOW_CHAIN_MAX ? r.chain_len : MEOW_CHAIN_MAX - 1;
|
||
char chain[MEOW_CHAIN_MAX];
|
||
memcpy(chain, r.chain, cl);
|
||
chain[cl] = 0;
|
||
printf("QUERY ok: app=%s act=%s depth=%u chain=%s\n", app_id, activity,
|
||
r.depth, cl ? chain : "(empty)");
|
||
return r.code == MEOW_OK ? 0 : 1;
|
||
}
|
||
if (rt == MSG_ERR) { print_err((const m_err_t *)rbuf); return 1; }
|
||
fprintf(stderr, "QUERY: unexpected resp type 0x%04x\n", rt);
|
||
return 1;
|
||
}
|
||
|
||
static int cmd_declare(const char *app_id, int nent, char **ents)
|
||
{
|
||
int fd = connect_path(g_sock);
|
||
if (fd < 0) return 1;
|
||
m_act_declare_t d;
|
||
memset(&d, 0, sizeof d);
|
||
strncpy(d.app_id, app_id, MEOW_APP_ID_MAX - 1);
|
||
int n = 0;
|
||
char *p = d.entries;
|
||
for (int i = 0; i < nent && n < 8; i++) {
|
||
size_t l = strlen(ents[i]);
|
||
if (l + 1 > (size_t)(d.entries + sizeof d.entries - p)) break;
|
||
memcpy(p, ents[i], l + 1);
|
||
p += l + 1;
|
||
n++;
|
||
}
|
||
d.count = (uint16_t)n;
|
||
uint32_t len = (uint32_t)(sizeof d.app_id + 2 + 2 + (p - d.entries));
|
||
uint8_t rbuf[256];
|
||
int rt = xchg(fd, MSG_ACT_DECLARE, 3, &d, len, rbuf, sizeof rbuf);
|
||
close(fd);
|
||
if (rt == MSG_ACK) {
|
||
m_ack_t a;
|
||
memcpy(&a, rbuf, sizeof a);
|
||
printf("DECLARE ok: app=%s n=%d code=%d\n", app_id, n, a.code);
|
||
return a.code == MEOW_OK ? 0 : 1;
|
||
}
|
||
if (rt == MSG_ERR) { print_err((const m_err_t *)rbuf); return 1; }
|
||
fprintf(stderr, "DECLARE: unexpected resp type 0x%04x\n", rt);
|
||
return 1;
|
||
}
|
||
|
||
static int cmd_focus(const char *app_id, const char *activity)
|
||
{
|
||
int fd = connect_path(g_sock);
|
||
if (fd < 0) return 1;
|
||
m_act_focus_t f;
|
||
memset(&f, 0, sizeof f);
|
||
strncpy(f.app_id, app_id, MEOW_APP_ID_MAX - 1);
|
||
strncpy(f.activity, activity, MEOW_ACT_MAX - 1);
|
||
uint8_t rbuf[256];
|
||
int rt = xchg(fd, MSG_ACT_FOCUS, 4, &f, sizeof f, rbuf, sizeof rbuf);
|
||
close(fd);
|
||
if (rt == MSG_ACK) {
|
||
m_ack_t a;
|
||
memcpy(&a, rbuf, sizeof a);
|
||
printf("FOCUS ok: app=%s act=%s code=%d\n", app_id, activity, a.code);
|
||
return a.code == MEOW_OK ? 0 : 1;
|
||
}
|
||
if (rt == MSG_ERR) { print_err((const m_err_t *)rbuf); return 1; }
|
||
fprintf(stderr, "FOCUS: unexpected resp type 0x%04x\n", rt);
|
||
return 1;
|
||
}
|
||
|
||
static int cmd_back(void)
|
||
{
|
||
int fd = connect_path(g_sock);
|
||
if (fd < 0) return 1;
|
||
uint8_t rbuf[256];
|
||
int rt = xchg(fd, MSG_ACT_BACK, 5, NULL, 0, rbuf, sizeof rbuf);
|
||
close(fd);
|
||
if (rt == MSG_ACK) {
|
||
m_ack_t a;
|
||
memcpy(&a, rbuf, sizeof a);
|
||
printf("BACK ok: code=%d\n", a.code);
|
||
return a.code == MEOW_OK ? 0 : 1;
|
||
}
|
||
if (rt == MSG_ERR) { print_err((const m_err_t *)rbuf); return 1; }
|
||
fprintf(stderr, "BACK: unexpected resp type 0x%04x\n", rt);
|
||
return 1;
|
||
}
|
||
|
||
/* M3:上报导航 → MSG_NAV,期望 MSG_ACK。
|
||
* nav report <app_id> <depth> <top> 上报栈深/顶层(MEOW_NAV_REPORT)
|
||
* nav exit <app_id> 请求退出(MEOW_NAV_EXIT) */
|
||
static int cmd_nav(const char *app_id, int exit_req, uint32_t depth, const char *top)
|
||
{
|
||
int fd = connect_path(g_sock);
|
||
if (fd < 0) return 1;
|
||
m_nav_t n;
|
||
memset(&n, 0, sizeof n);
|
||
strncpy(n.app_id, app_id, MEOW_APP_ID_MAX - 1);
|
||
n.nav_type = exit_req ? MEOW_NAV_EXIT : MEOW_NAV_REPORT;
|
||
n.depth = depth;
|
||
if (top) strncpy(n.current, top, MEOW_ACT_MAX - 1);
|
||
uint8_t rbuf[256];
|
||
int rt = xchg(fd, MSG_NAV, 10, &n, sizeof n, rbuf, sizeof rbuf);
|
||
close(fd);
|
||
if (rt == MSG_ACK) {
|
||
m_ack_t a;
|
||
memcpy(&a, rbuf, sizeof a);
|
||
printf("NAV ok: app=%s type=%u depth=%u top=\"%s\" code=%d\n",
|
||
app_id, n.nav_type, depth, n.current, a.code);
|
||
return a.code == MEOW_OK ? 0 : 1;
|
||
}
|
||
if (rt == MSG_ERR) { print_err((const m_err_t *)rbuf); return 1; }
|
||
fprintf(stderr, "NAV: unexpected resp type 0x%04x\n", rt);
|
||
return 1;
|
||
}
|
||
|
||
/* M2a:推送通知 → MSG_NOTIFY,期望 MSG_NOTIFY_ACK。 */
|
||
static int cmd_notify(const char *app_id, const char *category, const char *summary,
|
||
const char *body, uint32_t replaces_id, int ongoing, int media, int progress,
|
||
const char *target_activity)
|
||
{
|
||
int fd = connect_path(g_sock);
|
||
if (fd < 0) return 1;
|
||
m_notify_t n;
|
||
memset(&n, 0, sizeof n);
|
||
strncpy(n.app_id, app_id, MEOW_APP_ID_MAX - 1);
|
||
strncpy(n.category, category, sizeof n.category - 1);
|
||
strncpy(n.summary, summary, sizeof n.summary - 1);
|
||
if (body) strncpy(n.body, body, sizeof n.body - 1);
|
||
if (target_activity) strncpy(n.target_activity, target_activity, MEOW_ACT_MAX - 1);
|
||
n.replaces_id = replaces_id;
|
||
n.ongoing = ongoing ? 1 : 0;
|
||
n.media = media ? 1 : 0;
|
||
n.progress = progress;
|
||
n.expire_timeout = ongoing ? 0xFFFFFFFFu : 0; /* ONGOING 常驻 */
|
||
uint8_t rbuf[256];
|
||
int rt = xchg(fd, MSG_NOTIFY, 6, &n, sizeof n, rbuf, sizeof rbuf);
|
||
close(fd);
|
||
if (rt == MSG_NOTIFY_ACK) {
|
||
m_ack_t a;
|
||
memcpy(&a, rbuf, sizeof a);
|
||
printf("NOTIFY ok: id=%u code=%d\n", a.status, a.code);
|
||
return a.code == MEOW_OK ? 0 : 1;
|
||
}
|
||
if (rt == MSG_ERR) { print_err((const m_err_t *)rbuf); return 1; }
|
||
fprintf(stderr, "NOTIFY: unexpected resp type 0x%04x\n", rt);
|
||
return 1;
|
||
}
|
||
|
||
/* M2a:撤下通知 → MSG_NOTIFY_DISMISS,期望 MSG_ACK。 */
|
||
static int cmd_dismiss(uint32_t id)
|
||
{
|
||
int fd = connect_path(g_sock);
|
||
if (fd < 0) return 1;
|
||
uint8_t rbuf[256];
|
||
int rt = xchg(fd, MSG_NOTIFY_DISMISS, 7, &id, sizeof id, rbuf, sizeof rbuf);
|
||
close(fd);
|
||
if (rt == MSG_ACK) {
|
||
m_ack_t a;
|
||
memcpy(&a, rbuf, sizeof a);
|
||
printf("DISMISS ok: id=%u code=%d\n", id, a.code);
|
||
return a.code == MEOW_OK ? 0 : 1;
|
||
}
|
||
if (rt == MSG_ERR) { print_err((const m_err_t *)rbuf); return 1; }
|
||
fprintf(stderr, "DISMISS: unexpected resp type 0x%04x\n", rt);
|
||
return 1;
|
||
}
|
||
|
||
/* 查询当前内存活跃通知集 → MSG_NOTIFY_QUERY,期望 MSG_NOTIFY_QUERY_RESULT(打印 JSON 文本)。
|
||
* 可选 <app_id> 过滤(缺省=全部)。 */
|
||
static int cmd_querynotif(const char *app_id)
|
||
{
|
||
int fd = connect_path(g_sock);
|
||
if (fd < 0) return 1;
|
||
uint8_t rbuf[MEOW_IPC_MAX];
|
||
int rt = xchg(fd, MSG_NOTIFY_QUERY, 8, app_id, app_id ? (uint32_t)strlen(app_id) : 0,
|
||
rbuf, sizeof rbuf);
|
||
close(fd);
|
||
if (rt == MSG_NOTIFY_QUERY_RESULT) {
|
||
m_notify_query_result_t r;
|
||
memcpy(&r, rbuf, sizeof r);
|
||
uint32_t tl = r.text_len < sizeof r.text ? r.text_len : sizeof r.text - 1;
|
||
printf("QUERY ok: code=%d len=%u\n%s\n", r.code, (unsigned)tl, r.text);
|
||
return r.code == MEOW_OK ? 0 : 1;
|
||
}
|
||
if (rt == MSG_ERR) { print_err((const m_err_t *)rbuf); return 1; }
|
||
fprintf(stderr, "QUERY: unexpected resp type 0x%04x\n", rt);
|
||
return 1;
|
||
}
|
||
|
||
/* 一键清除(保留 ongoing/media/progress)→ MSG_NOTIFY_DISMISS_ALL,期望 MSG_ACK(status=清除数)。 */
|
||
static int cmd_dismissall(void)
|
||
{
|
||
int fd = connect_path(g_sock);
|
||
if (fd < 0) return 1;
|
||
uint8_t rbuf[256];
|
||
int rt = xchg(fd, MSG_NOTIFY_DISMISS_ALL, 9, NULL, 0, rbuf, sizeof rbuf);
|
||
close(fd);
|
||
if (rt == MSG_ACK) {
|
||
m_ack_t a;
|
||
memcpy(&a, rbuf, sizeof a);
|
||
printf("DISMISS_ALL ok: code=%d cleared=%u\n", 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, "DISMISS_ALL: unexpected resp type 0x%04x\n", rt);
|
||
return 1;
|
||
}
|
||
|
||
/* 阶段 D 附录 B:媒体会话上报 → MSG_META,期望 MSG_ACK。
|
||
* meta <app_id> <state> <pos_ms> <dur_ms> <title> <artist> <album> [artwork] */
|
||
static int cmd_meta(const char *app_id, uint32_t state, uint32_t pos, uint32_t dur,
|
||
const char *title, const char *artist, const char *album, const char *artwork)
|
||
{
|
||
int fd = connect_path(g_sock);
|
||
if (fd < 0) return 1;
|
||
m_meta_t mm;
|
||
memset(&mm, 0, sizeof mm);
|
||
strncpy(mm.app_id, app_id, MEOW_APP_ID_MAX - 1);
|
||
mm.state = state; mm.pos_ms = pos; mm.dur_ms = dur;
|
||
snprintf(mm.title, sizeof mm.title, "%s", title);
|
||
snprintf(mm.artist, sizeof mm.artist, "%s", artist);
|
||
snprintf(mm.album, sizeof mm.album, "%s", album);
|
||
if (artwork) snprintf(mm.artwork, sizeof mm.artwork, "%s", artwork);
|
||
uint8_t rbuf[256];
|
||
int rt = xchg(fd, MSG_META, 11, &mm, sizeof mm, rbuf, sizeof rbuf);
|
||
close(fd);
|
||
if (rt == MSG_ACK) {
|
||
m_ack_t a;
|
||
memcpy(&a, rbuf, sizeof a);
|
||
printf("META ok: app=%s state=%u code=%d\n", app_id, state, a.code);
|
||
return a.code == MEOW_OK ? 0 : 1;
|
||
}
|
||
if (rt == MSG_ERR) { print_err((const m_err_t *)rbuf); return 1; }
|
||
fprintf(stderr, "META: unexpected resp type 0x%04x\n", rt);
|
||
return 1;
|
||
}
|
||
|
||
/* 控制转发 → MSG_PLAYCTL(launcher 转发到最近活跃媒体会话),期望 MSG_ACK。
|
||
* playctl <cmd> [seek_ms] (cmd 1=播放 2=暂停 3=上一首 4=下一首 5=seek) */
|
||
static int cmd_playctl(uint32_t cmd, int64_t seek_ms)
|
||
{
|
||
int fd = connect_path(g_sock);
|
||
if (fd < 0) return 1;
|
||
m_playctl_t c;
|
||
memset(&c, 0, sizeof c);
|
||
c.cmd = cmd; c.seek_ms = seek_ms;
|
||
uint8_t rbuf[256];
|
||
int rt = xchg(fd, MSG_PLAYCTL, 12, &c, sizeof c, rbuf, sizeof rbuf);
|
||
close(fd);
|
||
if (rt == MSG_ACK) {
|
||
m_ack_t a;
|
||
memcpy(&a, rbuf, sizeof a);
|
||
printf("PLAYCTL ok: cmd=%u seek=%lld code=%d (status=%d)\n", cmd,
|
||
(long long)seek_ms, a.code, (int)a.status);
|
||
return a.code == MEOW_OK ? 0 : 1;
|
||
}
|
||
if (rt == MSG_ERR) { print_err((const m_err_t *)rbuf); return 1; }
|
||
fprintf(stderr, "PLAYCTL: unexpected resp type 0x%04x\n", rt);
|
||
return 1;
|
||
}
|
||
|
||
/* 读一帧(头+负载)→ 返回负载长;-1=连接断/坏帧。type/req_id 输出。 */
|
||
static int read_frame(int fd, uint16_t *type, uint32_t *req_id, uint8_t *rbuf, uint32_t rcap)
|
||
{
|
||
uint8_t rhdr[MEOW_IPC_HDR_LEN];
|
||
size_t off = 0;
|
||
while (off < sizeof rhdr) {
|
||
ssize_t r = read(fd, rhdr + off, sizeof rhdr - off);
|
||
if (r <= 0) return -1;
|
||
off += (size_t)r;
|
||
}
|
||
uint32_t magic, len; uint16_t ver;
|
||
memcpy(&magic, rhdr, 4); memcpy(&ver, rhdr + 4, 2); memcpy(type, rhdr + 6, 2);
|
||
memcpy(req_id, rhdr + 8, 4); memcpy(&len, rhdr + 12, 4);
|
||
if (magic != MEOW_IPC_MAGIC || ver != MEOW_IPC_VERSION) return -1;
|
||
if (len > rcap) len = rcap;
|
||
off = 0;
|
||
while (off < len) {
|
||
ssize_t r = read(fd, rbuf + off, len - off);
|
||
if (r <= 0) return -1;
|
||
off += (size_t)r;
|
||
}
|
||
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,
|
||
const char *title, const char *artist, const char *album,
|
||
const char *artwork)
|
||
{
|
||
int fd = connect_path(g_sock);
|
||
if (fd < 0) return 1;
|
||
m_hello_t h;
|
||
memset(&h, 0, sizeof h);
|
||
strncpy(h.app_id, app_id, MEOW_APP_ID_MAX - 1);
|
||
h.version = 1; h.flags = MF_PLAYBACK;
|
||
uint8_t rbuf[256];
|
||
int rt = xchg(fd, MSG_HELLO, 1, &h, sizeof h, rbuf, sizeof rbuf);
|
||
if (rt != MSG_ACK) { fprintf(stderr, "listen: HELLO resp 0x%04x\n", rt); close(fd); return 1; }
|
||
m_meta_t mm;
|
||
memset(&mm, 0, sizeof mm);
|
||
strncpy(mm.app_id, app_id, MEOW_APP_ID_MAX - 1);
|
||
mm.state = state; mm.pos_ms = pos; mm.dur_ms = dur;
|
||
snprintf(mm.title, sizeof mm.title, "%s", title);
|
||
snprintf(mm.artist, sizeof mm.artist, "%s", artist);
|
||
snprintf(mm.album, sizeof mm.album, "%s", album);
|
||
if (artwork) snprintf(mm.artwork, sizeof mm.artwork, "%s", artwork);
|
||
rt = xchg(fd, MSG_META, 2, &mm, sizeof mm, rbuf, sizeof rbuf);
|
||
if (rt != MSG_ACK) { fprintf(stderr, "listen: META resp 0x%04x\n", rt); close(fd); return 1; }
|
||
printf("listen: source=%s registered (state=%u) — awaiting PLAYCTL...\n", app_id, state);
|
||
fflush(stdout);
|
||
/* 阻塞读:等待 launcher 下发 MSG_PLAYCTL(同一连接)。 */
|
||
for (;;) {
|
||
uint16_t type; uint32_t req_id;
|
||
int n = read_frame(fd, &type, &req_id, rbuf, sizeof rbuf);
|
||
if (n < 0) { fprintf(stderr, "listen: conn closed\n"); close(fd); return 0; }
|
||
if (type == MSG_PLAYCTL && n >= (int)sizeof(m_playctl_t)) {
|
||
m_playctl_t c;
|
||
memcpy(&c, rbuf, sizeof c);
|
||
printf("RECV PLAYCTL: cmd=%u seek=%lld\n", c.cmd, (long long)c.seek_ms);
|
||
fflush(stdout);
|
||
} else {
|
||
printf("RECV frame type=0x%04x len=%d\n", type, n);
|
||
}
|
||
}
|
||
}
|
||
|
||
int main(int argc, char **argv)
|
||
{
|
||
const char *env = getenv("MEOW_IPC_SOCK");
|
||
g_sock = (env && env[0]) ? env : MEOW_IPC_PATH;
|
||
|
||
/* parse leading global options */
|
||
int i = 1;
|
||
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;
|
||
}
|
||
else break;
|
||
}
|
||
if (i >= argc) { fprintf(stderr, "bad args; try --help\n"); return 1; }
|
||
|
||
const char *cmd = argv[i];
|
||
if (!strcmp(cmd, "hello")) {
|
||
if (i + 1 >= argc) { fprintf(stderr, "hello needs <app_id>\n"); return 1; }
|
||
uint32_t ver = (i + 2 < argc) ? (uint32_t)strtoul(argv[i + 2], NULL, 0) : 1;
|
||
uint32_t flg = (i + 3 < argc) ? (uint32_t)strtoul(argv[i + 3], NULL, 0) : 0;
|
||
return cmd_hello(argv[i + 1], ver, flg);
|
||
}
|
||
if (!strcmp(cmd, "ping")) {
|
||
const char *app = (i + 1 < argc) ? argv[i + 1] : "meow_ipc";
|
||
return cmd_hello(app, 1, 0);
|
||
}
|
||
if (!strcmp(cmd, "query")) {
|
||
if (i + 2 >= argc) { fprintf(stderr, "query needs <app_id> <activity>\n"); return 1; }
|
||
return cmd_query(argv[i + 1], argv[i + 2]);
|
||
}
|
||
if (!strcmp(cmd, "declare")) {
|
||
if (i + 2 >= argc) { fprintf(stderr, "declare needs <app_id> <route_id=chain> [...]\n"); return 1; }
|
||
return cmd_declare(argv[i + 1], argc - i - 2, argv + i + 2);
|
||
}
|
||
if (!strcmp(cmd, "focus")) {
|
||
if (i + 2 >= argc) { fprintf(stderr, "focus needs <app_id> <activity>\n"); return 1; }
|
||
return cmd_focus(argv[i + 1], argv[i + 2]);
|
||
}
|
||
if (!strcmp(cmd, "back")) {
|
||
return cmd_back();
|
||
}
|
||
if (!strcmp(cmd, "notify")) {
|
||
/* notify <app_id> <category> <summary> [body] [replaces_id] [ongoing] [media] [progress] [target_activity] */
|
||
if (i + 2 >= argc) { fprintf(stderr, "notify needs <app_id> <category> <summary>\n"); return 1; }
|
||
const char *body = (i + 4 < argc) ? argv[i + 4] : "";
|
||
uint32_t rid = (i + 5 < argc) ? (uint32_t)strtoul(argv[i + 5], NULL, 0) : 0;
|
||
int ongoing = (i + 6 < argc) ? atoi(argv[i + 6]) : 0;
|
||
int media = (i + 7 < argc) ? atoi(argv[i + 7]) : 0;
|
||
int progress = (i + 8 < argc) ? atoi(argv[i + 8]) : -1;
|
||
const char *tgt = (i + 9 < argc) ? argv[i + 9] : "";
|
||
return cmd_notify(argv[i + 1], argv[i + 2], argv[i + 3], body, rid, ongoing, media, progress,
|
||
tgt);
|
||
}
|
||
if (!strcmp(cmd, "dismiss")) {
|
||
if (i + 1 >= argc) { fprintf(stderr, "dismiss needs <id>\n"); return 1; }
|
||
return cmd_dismiss((uint32_t)strtoul(argv[i + 1], NULL, 0));
|
||
}
|
||
if (!strcmp(cmd, "querynotif")) {
|
||
const char *app = (i + 1 < argc) ? argv[i + 1] : NULL;
|
||
return cmd_querynotif(app);
|
||
}
|
||
if (!strcmp(cmd, "dismissall")) {
|
||
return cmd_dismissall();
|
||
}
|
||
if (!strcmp(cmd, "meta")) {
|
||
/* meta <app_id> <state> <pos_ms> <dur_ms> <title> <artist> <album> [artwork] */
|
||
if (i + 7 >= argc) { fprintf(stderr, "meta needs <app_id> <state> <pos_ms> <dur_ms> <title> <artist> <album>\n"); return 1; }
|
||
uint32_t st = (uint32_t)strtoul(argv[i + 2], NULL, 0);
|
||
uint32_t pos = (uint32_t)strtoul(argv[i + 3], NULL, 0);
|
||
uint32_t dur = (uint32_t)strtoul(argv[i + 4], NULL, 0);
|
||
const char *aw = (i + 8 < argc) ? argv[i + 8] : NULL;
|
||
return cmd_meta(argv[i + 1], st, pos, dur, argv[i + 5], argv[i + 6], argv[i + 7], aw);
|
||
}
|
||
if (!strcmp(cmd, "listen")) {
|
||
/* listen <app_id> <state> <pos_ms> <dur_ms> <title> <artist> <album> [artwork] */
|
||
if (i + 7 >= argc) { fprintf(stderr, "listen needs <app_id> <state> <pos_ms> <dur_ms> <title> <artist> <album>\n"); return 1; }
|
||
uint32_t st = (uint32_t)strtoul(argv[i + 2], NULL, 0);
|
||
uint32_t pos = (uint32_t)strtoul(argv[i + 3], NULL, 0);
|
||
uint32_t dur = (uint32_t)strtoul(argv[i + 4], NULL, 0);
|
||
const char *aw = (i + 8 < argc) ? argv[i + 8] : NULL;
|
||
return cmd_listen(argv[i + 1], st, pos, dur, argv[i + 5], argv[i + 6], argv[i + 7], aw);
|
||
}
|
||
if (!strcmp(cmd, "playctl")) {
|
||
if (i + 1 >= argc) { fprintf(stderr, "playctl needs <cmd> [seek_ms]\n"); return 1; }
|
||
uint32_t cmd2 = (uint32_t)strtoul(argv[i + 1], NULL, 0);
|
||
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; }
|
||
int exit_req = (!strcmp(argv[i + 1], "exit") || !strcmp(argv[i + 1], "EXIT"));
|
||
if (i + 2 >= argc) { fprintf(stderr, "nav needs <app_id>\n"); return 1; }
|
||
uint32_t depth = (i + 3 < argc) ? (uint32_t)strtoul(argv[i + 3], NULL, 0) : 0;
|
||
const char *top = (i + 4 < argc) ? argv[i + 4] : "";
|
||
return cmd_nav(argv[i + 2], exit_req, depth, top);
|
||
}
|
||
fprintf(stderr, "unknown cmd %s; try --help\n", cmd);
|
||
return 1;
|
||
}
|