MeowOS for HiBy R3 Pro II — 公共发布版本(历史已重置)
• 全新单提交:清除含敏感信息的旧历史(sudo 口令、内网 IP、SSH 密钥路径、热点 SSID、设备 MAC) • 本地开发文档/测试记录/构建脚本全部移入 gitignored 的 devdocs/ 与 scripts/build/(不入库) • 保留:MeowOS 源码(launcher/watchdog/bootmenu/meow_svcd/SDK)、项目规范文档、mips-repo 增补组件:SDK 统一日志+通知 API、meow_svcd 服务管理器、mips-repo 软件仓库
This commit is contained in:
commit
245bee2946
136 files changed
+32730
No files matched your search
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* meow_ui_notify.c — MeowOS 应用通知 API(meow_notify_*,M2.53 最小可用版)。
|
||||
*
|
||||
* 定位:app 只需声明类型 + meow_notify_send() 即可发通知,SDK 管配额/活动表/降级。
|
||||
* 本切片为「SDK 侧应用可调用 API」第一个纵向切片——只做 SDK 层;通知中心 UI、
|
||||
* D-Bus 守护(org.freedesktop.Notifications)、TF 转储是后续独立任务。
|
||||
*
|
||||
* 行为约定(NOTIFICATION_SYSTEM.md §2/§7,2026-08-29 用户拍板):
|
||||
* - 类型注册:app 声明支持的类型,最多 MEOW_NOTIFY_MAX_TYPES(4)种;
|
||||
* meow_notify_send 也会自动注册未声明类型(受同样 4 种上限约束)。
|
||||
* - 配额:每类型活动(未过期)通知上限 MEOW_NOTIFY_MAX_PER_TYPE(4),满则丢最旧
|
||||
* (替换);媒体类型单例(每 app 仅保留最近 1 条,重发替换——可多媒体 App)。
|
||||
* - 超时语义 [实现定案]:timeout_ms > 0 为该毫秒数后过期;0 = 不自动过期(直到
|
||||
* meow_notify_cancel);-1 = 常驻(ONGOING),同样不自动过期。
|
||||
* - 落点:本阶段内部维护活动通知表 + 用既有 meow_log() 打日志作为落点;真实投递到
|
||||
* 通知中心 UI / D-Bus 是后续任务——meow_notify 结构已对齐 freedesktop 字段
|
||||
* (summary/body/…)为此预留,但不写死后端。
|
||||
*
|
||||
* 线程安全:pthread_mutex(同 meow_ui_log.c,launcher 前后台进程都会在已链 -lpthread
|
||||
* 的应用里调用)。不依赖 /dev/fb0、不依赖板子,可宿主编译。内部只复用
|
||||
* meow_ui_time_ms()(单调毫秒)与 meow_log()(统一日志)两个既有 SDK 能力。
|
||||
*/
|
||||
#include "meow_ui.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <pthread.h>
|
||||
|
||||
/* ---- 内部活动表尺寸(配额见 meow_ui.h) ---- */
|
||||
#define MEOW_NOTIFY_MAX_ACTIVE (MEOW_NOTIFY_MAX_TYPES * MEOW_NOTIFY_MAX_PER_TYPE)
|
||||
|
||||
/* 活动通知槽(内部;标题/正文等复制副本,不持 app 外部指针,避免栈字符串悬垂) */
|
||||
typedef struct {
|
||||
uint32_t id; /* 唯一 id;0=空闲槽 */
|
||||
int active; /* 1=活动(未取消未过期),0=空闲 */
|
||||
meow_notify_category cat;
|
||||
meow_notify_urgency urg;
|
||||
int timeout_ms;
|
||||
uint64_t created_ms;
|
||||
char title[64];
|
||||
char body[128];
|
||||
char target_activity[32];
|
||||
int progress; /* 0-100;-1=无进度条 */
|
||||
} notify_slot_t;
|
||||
|
||||
static pthread_mutex_t g_mu = PTHREAD_MUTEX_INITIALIZER;
|
||||
static notify_slot_t g_slots[MEOW_NOTIFY_MAX_ACTIVE];
|
||||
static int g_cats[MEOW_NOTIFY_MAX_TYPES]; /* 已声明类型(按注册顺序) */
|
||||
static int g_cat_n = 0;
|
||||
static uint32_t g_next_id = 1;
|
||||
|
||||
/* 字符串表(与枚举序号对齐) */
|
||||
static const char *g_urg_str[] = { "low", "normal", "high", "critical" };
|
||||
static const char *g_cat_str[] = { "general", "message", "email", "event", "progress", "media" };
|
||||
|
||||
/* ---- 内部工具(以下均须持有 g_mu 时调用) ---- */
|
||||
|
||||
/* 该分类当前活动数。 */
|
||||
static int notify_count_cat(meow_notify_category cat)
|
||||
{
|
||||
int i, n = 0;
|
||||
for (i = 0; i < MEOW_NOTIFY_MAX_ACTIVE; i++)
|
||||
if (g_slots[i].active && g_slots[i].cat == cat) n++;
|
||||
return n;
|
||||
}
|
||||
|
||||
/* 该分类是否已被 app 声明。 */
|
||||
static int notify_is_declared(meow_notify_category cat)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < g_cat_n; i++)
|
||||
if (g_cats[i] == (int)cat) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 清掉已过期活动槽(TTL 用 meow_ui_time_ms() 单调毫秒)。 */
|
||||
static void notify_purge_expired(void)
|
||||
{
|
||||
uint64_t now = meow_ui_time_ms();
|
||||
int i;
|
||||
for (i = 0; i < MEOW_NOTIFY_MAX_ACTIVE; i++) {
|
||||
notify_slot_t *s = &g_slots[i];
|
||||
if (!s->active) continue;
|
||||
if (s->timeout_ms > 0 && (now - s->created_ms) >= (uint64_t)s->timeout_ms) {
|
||||
s->active = 0;
|
||||
s->id = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 填充一个活动槽(改前须确认槽空闲或已被显式复用)。 */
|
||||
static void notify_fill(notify_slot_t *s, const meow_notify *n, uint32_t id, uint64_t now)
|
||||
{
|
||||
s->id = id;
|
||||
s->active = 1;
|
||||
s->cat = n->cat;
|
||||
s->urg = ((int)n->urgency >= 0 && (int)n->urgency < (int)MEOW_NOTIFY_URG_COUNT)
|
||||
? n->urgency : MEOW_NOTIFY_URG_NORMAL;
|
||||
s->timeout_ms = n->timeout_ms;
|
||||
s->created_ms = now;
|
||||
snprintf(s->title, sizeof s->title, "%s", n->title ? n->title : "");
|
||||
snprintf(s->body, sizeof s->body, "%s", n->body ? n->body : "");
|
||||
snprintf(s->target_activity, sizeof s->target_activity, "%s",
|
||||
n->target_activity ? n->target_activity : "");
|
||||
s->progress = (n->progress < -1) ? -1 : (n->progress > 100 ? 100 : n->progress);
|
||||
}
|
||||
|
||||
/* 落点:本阶段用统一日志标出一条投递;真实后端(通知中心 UI / D-Bus)后续替换此处。 */
|
||||
static void notify_deliver(const notify_slot_t *s, const char *ev)
|
||||
{
|
||||
meow_log(MEOW_LOG_INFO, "[notify] %s id=%u type=%s urgency=%s title=%s",
|
||||
ev, (unsigned)s->id, meow_notify_category_str(s->cat),
|
||||
meow_notify_urgency_str(s->urg), s->title);
|
||||
}
|
||||
|
||||
/* ============================================================ */
|
||||
/* 公共 API(见 meow_ui.h §9.1) */
|
||||
/* ============================================================ */
|
||||
|
||||
int meow_notify_register_type(meow_notify_category cat)
|
||||
{
|
||||
int rc = 0;
|
||||
if ((int)cat < 0 || (int)cat >= (int)MEOW_NOTIFY_CAT_COUNT) return -1;
|
||||
pthread_mutex_lock(&g_mu);
|
||||
if (notify_is_declared(cat)) rc = -1; /* 重复声明 */
|
||||
else if (g_cat_n >= MEOW_NOTIFY_MAX_TYPES) rc = -1; /* 超 4 种上限 */
|
||||
else g_cats[g_cat_n++] = (int)cat;
|
||||
pthread_mutex_unlock(&g_mu);
|
||||
return rc;
|
||||
}
|
||||
|
||||
uint32_t meow_notify_send(const meow_notify *n)
|
||||
{
|
||||
uint32_t id;
|
||||
uint64_t now, oldest;
|
||||
int i, slot_idx, count, oldest_idx;
|
||||
|
||||
if (!n || (int)n->cat < 0 || (int)n->cat >= (int)MEOW_NOTIFY_CAT_COUNT) return 0;
|
||||
if (!n->title) return 0;
|
||||
|
||||
pthread_mutex_lock(&g_mu);
|
||||
notify_purge_expired();
|
||||
|
||||
/* 未声明类型:自动注册(≤4 种,超过则拒绝本次发送) */
|
||||
if (!notify_is_declared(n->cat)) {
|
||||
if (g_cat_n >= MEOW_NOTIFY_MAX_TYPES) {
|
||||
pthread_mutex_unlock(&g_mu);
|
||||
return 0;
|
||||
}
|
||||
g_cats[g_cat_n++] = (int)n->cat;
|
||||
}
|
||||
|
||||
slot_idx = -1;
|
||||
if (n->cat == MEOW_NOTIFY_CAT_MEDIA) {
|
||||
/* 媒体单例:复用已存在的媒体槽(重发替换同 app 上一条媒体通知) */
|
||||
for (i = 0; i < MEOW_NOTIFY_MAX_ACTIVE; i++)
|
||||
if (g_slots[i].active && g_slots[i].cat == MEOW_NOTIFY_CAT_MEDIA) { slot_idx = i; break; }
|
||||
} else {
|
||||
count = notify_count_cat(n->cat);
|
||||
if (count >= MEOW_NOTIFY_MAX_PER_TYPE) {
|
||||
/* 满则丢该类型最旧一条(替换) */
|
||||
oldest_idx = -1;
|
||||
oldest = UINT64_MAX;
|
||||
for (i = 0; i < MEOW_NOTIFY_MAX_ACTIVE; i++)
|
||||
if (g_slots[i].active && g_slots[i].cat == n->cat && g_slots[i].created_ms < oldest) {
|
||||
oldest = g_slots[i].created_ms;
|
||||
oldest_idx = i;
|
||||
}
|
||||
if (oldest_idx >= 0) { g_slots[oldest_idx].active = 0; g_slots[oldest_idx].id = 0; }
|
||||
}
|
||||
}
|
||||
if (slot_idx < 0) {
|
||||
for (i = 0; i < MEOW_NOTIFY_MAX_ACTIVE; i++)
|
||||
if (!g_slots[i].active) { slot_idx = i; break; }
|
||||
}
|
||||
if (slot_idx < 0) {
|
||||
/* 理论不可达(上方已丢最旧腾位);兜底防御 */
|
||||
pthread_mutex_unlock(&g_mu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
now = meow_ui_time_ms();
|
||||
id = g_next_id++;
|
||||
if (g_next_id == 0) g_next_id = 1; /* 防回绕 */
|
||||
notify_fill(&g_slots[slot_idx], n, id, now);
|
||||
notify_deliver(&g_slots[slot_idx], "send");
|
||||
pthread_mutex_unlock(&g_mu);
|
||||
return id;
|
||||
}
|
||||
|
||||
int meow_notify_cancel(uint32_t id)
|
||||
{
|
||||
int i, rc = 0;
|
||||
if (id == 0) return 0;
|
||||
pthread_mutex_lock(&g_mu);
|
||||
notify_purge_expired();
|
||||
for (i = 0; i < MEOW_NOTIFY_MAX_ACTIVE; i++) {
|
||||
if (g_slots[i].active && g_slots[i].id == id) {
|
||||
g_slots[i].active = 0;
|
||||
g_slots[i].id = 0;
|
||||
rc = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&g_mu);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int meow_notify_active_count(void)
|
||||
{
|
||||
int i, n = 0;
|
||||
pthread_mutex_lock(&g_mu);
|
||||
notify_purge_expired();
|
||||
for (i = 0; i < MEOW_NOTIFY_MAX_ACTIVE; i++)
|
||||
if (g_slots[i].active) n++;
|
||||
pthread_mutex_unlock(&g_mu);
|
||||
return n;
|
||||
}
|
||||
|
||||
int meow_notify_count_in_type(meow_notify_category cat)
|
||||
{
|
||||
int n;
|
||||
pthread_mutex_lock(&g_mu);
|
||||
notify_purge_expired();
|
||||
n = notify_count_cat(cat);
|
||||
pthread_mutex_unlock(&g_mu);
|
||||
return n;
|
||||
}
|
||||
|
||||
int meow_notify_has_active(void)
|
||||
{
|
||||
int i, rc = 0;
|
||||
pthread_mutex_lock(&g_mu);
|
||||
notify_purge_expired();
|
||||
for (i = 0; i < MEOW_NOTIFY_MAX_ACTIVE; i++)
|
||||
if (g_slots[i].active) { rc = 1; break; }
|
||||
pthread_mutex_unlock(&g_mu);
|
||||
return rc;
|
||||
}
|
||||
|
||||
const char *meow_notify_urgency_str(meow_notify_urgency u)
|
||||
{
|
||||
/* 钳位(同 meow_ui_log.c 防越界枚举触发 OOB 读) */
|
||||
if ((int)u >= 0 && (int)u < (int)MEOW_NOTIFY_URG_COUNT) return g_urg_str[u];
|
||||
return g_urg_str[MEOW_NOTIFY_URG_NORMAL];
|
||||
}
|
||||
|
||||
const char *meow_notify_category_str(meow_notify_category c)
|
||||
{
|
||||
if ((int)c >= 0 && (int)c < (int)MEOW_NOTIFY_CAT_COUNT) return g_cat_str[c];
|
||||
return g_cat_str[MEOW_NOTIFY_CAT_GENERAL];
|
||||
}
|
||||
Reference in new issue
Block a user