WIP checkpoint (M2.53→M2.54 工作区): 字体归MeowOS/fonts+SDK跟随+多字体(进行中)+boot板载+状态栏28+亮度/音量修复+文档刷新

This commit is contained in:
MeowOS Dev committed 2026-08-30 03:27:24 +08:00
1 parent 245bee2946
commit 064635b3c4
44 files changed
+2834 -308

No files matched your search

+243
View File
@@ -0,0 +1,243 @@
/*
* meow_notify_ipc.c — MeowOS SDK 通知 → launcher IPC 桥(任务 #5,SDK↔IPC 关键断点)。
*
* 让 meow_notify_send() 的通知真正经 AF_UNIX IPC 送到 launcher 中央存储(meow_notif_post)
* 并回读 MSG_NOTIFY_ACK 拿 launcher 分配的通知 id(nid)。协议帧与负载结构复用 launcher 的
* meow_ipc.h(同源一份、杜绝两端漂移):
* - 帧头 16B:magic=0x4D4F5701 / version=1 / type / req_id / len(全小端 LE,mipsel 原生);
* - MSG_NOTIFY(0x0020) ↑ 负载 = m_notify_t;MSG_NOTIFY_ACK(0x0021) ↓ 负载 = m_ack_t(status=nid);
* - MSG_NOTIFY_DISMISS(0x0022) ↑ 负载 = uint32 nid。
*
* 设计要点:
* - 只做「一次性 connect + send + read ACK」,不维护连接/订阅循环(与 meow_ipc.c 客户端同型);
* - 映射层(SDK 枚举 ↔ IPC 字段)集中在本文件:分类枚举 CAT_* ↔ 字符串
* "general"/"message"/"email"/"event"/"progress"/"media";timeout_ms → expire_timeout:
* >0 该毫秒、0=不自动过期、-1(ONGOING)=0xFFFFFFFF(并置 ongoing=1);媒体类型置 media=1;
* - 优雅降级:无 socket(connect 失败)/写失败/ACK 超时(SO_RCVTIMEO)/非 ACK 应答 →
* 返回 0(调用方 meow_ui_notify.c 保留本地 id 兜底),绝不崩;
* - 线程安全:app_id 经 pthread_once 缓存;socket 路径每次调用读 env MEOW_IPC_SOCK 覆盖
* (宿主/test 用),缺省 meow_ipc.h 的 MEOW_IPC_PATH(/usr/data/meow_ipc.sock)。
*
* 约定:与 meow_ui_notify.c 同风格(M2.x 中文注释);本文件为纯传输层,不调 meow_log,
* 失败诊断由调用方 meow_ui_notify.c 的 notify_deliver() 统一打点。
*/
#include "meow_notify_ipc.h"
#include "meow_ipc.h" /* launcher 端共享协议:帧头/m_notify_t/m_ack_t/MSG_* —— 防漂移 */
#include <stdio.h> /* snprintf */
#include <stdlib.h> /* getenv */
#include <string.h>
#include <errno.h> /* EINTR */
#include <unistd.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/time.h>
/* ACK 读超时(ms):launcher 主循环为 IPC 单写者(eventfd 唤醒,正常 <10ms 响应),
* 放宽容错 500ms;超时视为降级(返回 0,不崩)。 */
#define MEOW_NOTIFY_IPC_TIMEOUT_MS 500
/* app_id(env MEOW_LOG_APP,缺省 "app";与 meow_ui_log.c 的取值口径一致)。 */
static pthread_once_t g_once = PTHREAD_ONCE_INIT;
static char g_app_id[MEOW_APP_ID_MAX] = "app";
static void ipc_init(void)
{
const char *s = getenv("MEOW_LOG_APP");
if (s && s[0]) snprintf(g_app_id, sizeof g_app_id, "%s", s);
}
/* socket 路径:优先 env MEOW_IPC_SOCK(宿主/qemu sim 覆盖),缺省 MEOW_IPC_PATH。 */
static const char *ipc_sock_path(void)
{
const char *s = getenv("MEOW_IPC_SOCK");
return (s && s[0]) ? s : MEOW_IPC_PATH;
}
const char *meow_notify_ipc_app_id(void)
{
pthread_once(&g_once, ipc_init);
return g_app_id;
}
/* ============================================================ */
/* 映射层: SDK meow_notify 枚举/字段 ↔ IPC m_notify_t 字段 */
/* ============================================================ */
/* SDK 分类枚举 → IPC 字符串(与 meow_ui.h §9.1 的 MEOW_NOTIFY_CAT_* 序号对齐;
* 越界钳位为 "general"——同 meow_notify_category_str 的防御语义)。 */
static const char *cat_to_str(meow_notify_category cat)
{
static const char *t[] = { "general", "message", "email", "event", "progress", "media" };
if ((int)cat >= 0 && (int)cat < (int)MEOW_NOTIFY_CAT_COUNT) return t[cat];
return t[MEOW_NOTIFY_CAT_GENERAL];
}
/* SDK timeout_ms → IPC expire_timeout(存储/显示端语义):
* >0 → 该毫秒数后过期;0 → 不自动过期(NEVER);<0 → 常驻(ONGOING)= 0xFFFFFFFF。 */
static uint32_t timeout_to_expire(int timeout_ms)
{
if (timeout_ms < 0) return 0xFFFFFFFFu; /* ONGOING:常驻,不自动过期 */
return (uint32_t)timeout_ms; /* 0(NEVER)/ >0(毫秒) */
}
/* 构建 IPC 负载 m_notify_t(app_icon/actions/hints v1 留空,M2b D-Bus 桥再填)。
* 仅在持有调用者互斥之外、发送前调用(纯 CPU 拷贝,不阻塞)。 */
static void build_m_notify(const meow_notify *n, uint32_t replaces_id, m_notify_t *mn)
{
memset(mn, 0, sizeof *mn);
snprintf(mn->app_id, sizeof mn->app_id, "%s", meow_notify_ipc_app_id());
snprintf(mn->category, sizeof mn->category, "%s", cat_to_str(n->cat));
snprintf(mn->summary, sizeof mn->summary, "%s", n->title ? n->title : "");
snprintf(mn->body, sizeof mn->body, "%s", n->body ? n->body : "");
snprintf(mn->target_activity, sizeof mn->target_activity, "%s",
n->target_activity ? n->target_activity : "");
mn->replaces_id = replaces_id; /* 0=新建;>0=更新(不存在则落为新建,见 launcher) */
mn->expire_timeout = timeout_to_expire(n->timeout_ms);
mn->progress = (n->progress < -1) ? -1 : (n->progress > 100 ? 100 : n->progress);
mn->media = (n->cat == MEOW_NOTIFY_CAT_MEDIA) ? 1 : 0;
mn->ongoing = (n->timeout_ms == MEOW_NOTIFY_TIMEOUT_ONGOING) ? 1 : 0;
}
/* ============================================================ */
/* 传输层(AF_UNIX SOCK_STREAM;一次性 connect + send + read) */
/* ============================================================ */
/* 连接 socket。失败返回 -1(不崩;调用方降级)。 */
static int conn_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;
}
/* 读满 n 字节(超时/断开返回 0)。 */
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 0;
if (len && payload && !write_all(fd, payload, len)) return 0;
return 1;
}
/* 给 conn 设 ACK 读超时(失败忽略——超时降级由 read_all 兜底)。 */
static void set_ack_timeout(int fd)
{
struct timeval tv;
tv.tv_sec = MEOW_NOTIFY_IPC_TIMEOUT_MS / 1000;
tv.tv_usec = (MEOW_NOTIFY_IPC_TIMEOUT_MS % 1000) * 1000;
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
}
uint32_t meow_notify_ipc_send(const meow_notify *n, uint32_t replaces_id)
{
m_notify_t mn;
int fd;
if (!n) return 0;
build_m_notify(n, replaces_id, &mn);
fd = conn_path(ipc_sock_path());
if (fd < 0) return 0; /* 无 socket:优雅降级 */
set_ack_timeout(fd);
if (!send_frame(fd, MSG_NOTIFY, 0x4E4F5449u /* "NOTI" */, &mn, (uint32_t)sizeof mn)) {
close(fd); /* 写失败:降级 */
return 0;
}
/* 读回 16B 应答头 + m_ack_t,取 launcher 分配的通知 id(a.status)。 */
uint8_t rhdr[MEOW_IPC_HDR_LEN];
uint32_t nid = 0;
if (read_all(fd, rhdr, sizeof rhdr)) {
uint32_t rmagic, rlen;
uint16_t rver, rtype;
memcpy(&rmagic, rhdr + 0, 4);
memcpy(&rver, rhdr + 4, 2);
memcpy(&rtype, rhdr + 6, 2);
memcpy(&rlen, rhdr + 12, 4);
if (rmagic == MEOW_IPC_MAGIC && rver == MEOW_IPC_VERSION &&
rtype == MSG_NOTIFY_ACK && rlen >= sizeof(m_ack_t)) {
uint8_t pay[sizeof(m_ack_t)];
if (read_all(fd, pay, sizeof(m_ack_t))) {
m_ack_t a;
memcpy(&a, pay, sizeof a);
if (a.code == MEOW_OK) nid = a.status; /* 落库成功 → nid */
}
}
}
close(fd);
return nid; /* 非 ACK/超时 → 0(降级) */
}
void meow_notify_ipc_dismiss(uint32_t nid)
{
int fd;
if (nid == 0) return;
fd = conn_path(ipc_sock_path());
if (fd < 0) return; /* 无 socket:静默 */
set_ack_timeout(fd);
if (!send_frame(fd, MSG_NOTIFY_DISMISS, 0x4E4F5444u /* "NOTD" */, &nid, (uint32_t)sizeof nid)) {
close(fd); /* 写失败:静默 */
return;
}
/* 读回应答(launcher 对 DISMISS 回 MSG_ACK,见 meow_ipc.c cmd_dismiss)。虽为
* best-effort,但读回确保 launcher 已处理完撤下再关连接(避免写后即关的丢帧竞态)。 */
uint8_t rhdr[MEOW_IPC_HDR_LEN];
if (read_all(fd, rhdr, sizeof rhdr)) {
uint32_t rmagic, rlen;
uint16_t rver, rtype;
memcpy(&rmagic, rhdr + 0, 4);
memcpy(&rver, rhdr + 4, 2);
memcpy(&rtype, rhdr + 6, 2);
memcpy(&rlen, rhdr + 12, 4);
if (rmagic == MEOW_IPC_MAGIC && rver == MEOW_IPC_VERSION &&
rtype == MSG_ACK && rlen >= sizeof(m_ack_t)) {
uint8_t pay[sizeof(m_ack_t)];
(void)read_all(fd, pay, sizeof(m_ack_t)); /* 读到即确认;内容不关心 */
}
}
close(fd);
}