sdk: 同步主仓新增模块 cachegov/lazygrid/select/net 通道
This commit is contained in:
1 parent
0af6af7c7e
commit
64cde13c8d
15 files changed
+1326
-10
No files matched your search
+3
-2
@@ -14,8 +14,9 @@ AR="${AR:-/usr/bin/ar}" # 系统 GNU ar 即可归档 mipsel ELF 对象(架
|
||||
rm -f meow_ui.a *.o
|
||||
|
||||
# meow_font.c = 预渲染位图字体运行时(自包含:只含 meow_font.h + 标准头;最小 zlib ABI 自声明,
|
||||
# 链接时用 sysroot 的 libz.a),随 SDK 一并 vendored。新模块(box/anim/scroll/str)随主仓增量并入。
|
||||
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_pinyin 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 meow_ui_audio stb_image meow_font meow_ui_box meow_ui_anim meow_ui_scroll meow_ui_str"
|
||||
# 链接时用 sysroot 的 libz.a),随 SDK 一并 vendored。新模块(box/anim/scroll/str + cachegov/lazygrid/
|
||||
# select/net_ipc)随主仓增量并入。
|
||||
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_pinyin 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 meow_ui_audio stb_image meow_font meow_ui_box meow_ui_anim meow_ui_scroll meow_ui_str meow_cachegov meow_lazygrid meow_select meow_net_ipc"
|
||||
CFLAGS="-Os -static -Iinclude -I."
|
||||
for f in $OBJS; do
|
||||
echo "== cc $f =="
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* meow_cachegov.h — 磁盘缓存 page-cache 治理 SDK 库(A 的主导/治本侧)。
|
||||
*
|
||||
* 归属(devdocs/plan/ARCHITECTURE_PRINCIPLES.md ③混合,2026-09-02 用户定案):
|
||||
* - A(DONTNEED 热路径,需要 read_pos) → 随 SDK 库(本文件,进程内直读 read_pos,无 IPC)。
|
||||
* - C(设备级 /proc/meminfo Cached 监控降载)→ 归系统服务 meow_svc_perf;本模块只读其共享状态
|
||||
* 文件(MEOW_CACHEGOV_C_FILE)作为「降载」信号,供 App 查询。
|
||||
* - App(如 ncm)侧只调本库接口:登记文件 + read_pos 回调 + tick + drop_finished + 查询降载,
|
||||
* 不写自己的治理算法。
|
||||
*
|
||||
* 权威方案:devdocs/plan/OOM_PAGECACHE_GOVERNANCE.md。红线:**L2(丢 read_pos 前方)禁止**,
|
||||
* 本库绝不 drop `[read_pos, ...)` 任何未解码区。
|
||||
*
|
||||
* 分级(§2.2):
|
||||
* L0(默认,安全): 曲终/commit 后对整文件 posix_fadvise(DONTNEED)——播放器已退出解码循环。
|
||||
* L1(可选,主杠杆): 播放中丢已读前缀 `[0, read_pos-keep_hot)`,保 keep_hot 窗口供倒带;
|
||||
* 仅 drop_mode>=1 且 A 未回退到 ≥2 时执行。
|
||||
* L2(禁止,红线): 绝不丢 read_pos 前方/当前未解码页。
|
||||
*
|
||||
* underrun 回退(§2.3,SDK 内置):App 在 pread 慢(数据在盘上但 >threshold ms)处上报
|
||||
* meow_cachegov_note_refault();本库滚窗 10s,超门限逐级回退(放大 keep_hot → 降 L0 → 关 A)。
|
||||
*
|
||||
* 接口按 OOM 文档 §4.2 + §4.4;值语义与 mem_watch 的「滞回三档」风格对齐(自含:pthread + meow_log)。
|
||||
*/
|
||||
#ifndef MEOW_CACHEGOV_H
|
||||
#define MEOW_CACHEGOV_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct meow_cachegov_cfg {
|
||||
int enabled; /* 总开关:0=A 完全不丢页(register/tick/drop_finished 全部 no-op) */
|
||||
int cached_threshold_kb; /* 保留:C 侧阈值归 svc_perf(本库恒 0,只读不控,防误用) */
|
||||
int dirty_warn_kb; /* 保留:同上(svc_perf 用;本库只透传日志) */
|
||||
size_t keep_hot_bytes; /* L1 保留窗口:read_pos 之后保多少不丢(回退时自动放大) */
|
||||
size_t min_hot_ahead; /* L2 红线:read_pos 前方至少保多少——仅防御注释,**绝不参与 drop** */
|
||||
int drop_mode; /* 0=仅曲终(L0);1=曲终+播放中丢已读前缀(L0+L1,配 seek 回退) */
|
||||
int refault_backoff; /* 10s 窗口内 refault 触发回退的门限(次数) */
|
||||
} meow_cachegov_cfg_t;
|
||||
|
||||
/* 登记本次治理的缓存文件(正式缓存文件路径 + 大小 + read_pos 回调 + 回调 ctx)。
|
||||
* 返回 0=ok / 负=错。重复 path 幂等(替换条目)。 */
|
||||
int meow_cachegov_register(const char *path, uint64_t size,
|
||||
uint64_t (*read_pos_fn)(void *ctx), void *ctx);
|
||||
|
||||
/* 注销该 path 的治理条目(曲终/切歌后清理)。返回 0=找到并删 / 负=不在册。 */
|
||||
int meow_cachegov_unregister(const char *path);
|
||||
|
||||
/* 周期调用(播放循环节拍 / 监控线程均可):
|
||||
* ① 按回退档执行 L1 丢弃(丢已读前缀 [0, read_pos-keep_hot));
|
||||
* ② 评估 refault 滚窗做逐级回退。 */
|
||||
int meow_cachegov_tick(void);
|
||||
|
||||
/* 曲终/commit 后:对刚播完文件做整文件 L0 DONTNEED + 注销该条目。
|
||||
* 回退档>=3(A 整体关闭)时不丢页(L0 也被关,见 §2.3 步骤 3)。 */
|
||||
int meow_cachegov_drop_finished(const char *path);
|
||||
|
||||
/* 下载/预取方查询(1=应暂停)。数据源 = A 侧回退硬暂停 OR svc_perf C(Cached 超阈值)降载。 */
|
||||
int meow_cachegov_download_paused(void);
|
||||
int meow_cachegov_prefetch_paused(void);
|
||||
|
||||
/* 设置/读取治理配置(运行时覆盖编译期默认)。 */
|
||||
int meow_cachegov_set_cfg(const meow_cachegov_cfg_t *cfg);
|
||||
int meow_cachegov_get_cfg(meow_cachegov_cfg_t *cfg);
|
||||
|
||||
/* ---- refault 上报 + A 回退状态(underrun 检测,§2.3)---- */
|
||||
/* App 在 pread 慢(数据已在盘上、读出 >threshold ms)处上报一次 refault。线程安全。 */
|
||||
void meow_cachegov_note_refault(void);
|
||||
/* A 当前回退档:0=正常 1=keep_hot 放大 2=降 L0(L1 关) 3=A 关。 */
|
||||
int meow_cachegov_backoff_level(void);
|
||||
|
||||
/* ---- 宿主测试桩(设备永不调用;-1=真实时钟,>=0 时用注入值替代 monotonic 时钟)---- */
|
||||
void meow_cachegov_test_set_time_ms(int64_t t);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* MEOW_CACHEGOV_H */
|
||||
@@ -35,6 +35,12 @@
|
||||
/* 列表编码上限(count 个 NUL 字符串时,distinct 工作缓冲上限) */
|
||||
#define MEOW_IPC_CARGO 1024u /* 守护进程入队拷贝上限(可小于 MEOW_IPC_MAX) */
|
||||
|
||||
/* ---- page-cache 治理(OOM_PAGECACHE_GOVERNANCE.md,③混合)----
|
||||
* meow_svc_perf(C:/proc/meminfo Cached 监控降载)把「是否暂停下载/预取」写入此共享状态文件
|
||||
* (一行式 key=value;App/SDK meow_cachegov 只读,系统级单点,不重复起线程)。
|
||||
* 路径可被 env MEOW_CACHEGOV_C_FILE 覆盖(宿主测试隔离)。 */
|
||||
#define MEOW_CACHEGOV_C_FILE "/tmp/meow_cachegov_c.state"
|
||||
|
||||
/* ---- 消息类型(FRAMEWORK_DESIGN §1 表)---- */
|
||||
enum {
|
||||
MSG_NONE = 0x0000,
|
||||
@@ -601,4 +607,34 @@ typedef struct m_net_dns_result {
|
||||
char text[MEOW_IPC_MAX - 8];
|
||||
} m_net_dns_result_t;
|
||||
|
||||
/* ---- 期 4:SDK 网络自愿通道(NETMGR_DESIGN §6.3/§4;0xEE/0xEF)----
|
||||
* 一、meow_net_policy_check(host):框架 App 联网前自查 host 是否被 netmgrd 策略放行——
|
||||
* 服务端以调用方 app_id(msg.app_id)→ 实体 app:<pkg>,评估 policy_net_blocked(entity)
|
||||
* || dns_query_domain_denied(entity, host),放行=allow 1 / 拒绝=block 0;netmgrd 不在 → SDK 侧放行。
|
||||
* 二、meow_net_report(rx,tx):App 自愿上报本进程收发字节数(内核无 per-socket/per-uid 计数,
|
||||
* §4 诚实声明的「字节级记账走 SDK 自报」);netmgrd 记入 per-实体统计(status JSON entity_bytes)。
|
||||
* 读级/上报任意调用者(自愿自报,无副作用;block 判定由服务端策略引擎权威执行)。
|
||||
* (0x00EE 曾留作「事件推送 订阅制」,期 4 起改作 SDK 通道;事件推送期 2 从未实装。) */
|
||||
enum { /* 与主 enum 分列,追加式不改既有行 */
|
||||
MSG_NET_SDK = 0x00EE, /* ↑ op=0 策略检查 / op=1 字节上报 */
|
||||
MSG_NET_SDK_RESULT = 0x00EF /* ↓ code + (CHECK)allow + 状态文本 */
|
||||
};
|
||||
enum { NET_SDK_CHECK = 0, NET_SDK_REPORT = 1 };
|
||||
|
||||
typedef struct m_net_sdk {
|
||||
uint32_t op; /* NET_SDK_CHECK / NET_SDK_REPORT */
|
||||
char app_id[MEOW_APP_ID_MAX]; /* 调用方 application(SDK 用 env MEOW_LOG_APP;CLI/测试可任意) */
|
||||
char host[64]; /* CHECK:待查域名 */
|
||||
uint32_t rx; /* REPORT:本进程累计 RX 字节 */
|
||||
uint32_t tx; /* REPORT:本进程累计 TX 字节 */
|
||||
} m_net_sdk_t; /* 4+32+64+4+4 = 108 B */
|
||||
|
||||
typedef struct m_net_sdk_result {
|
||||
uint32_t code; /* MEOW_OK / MEOW_ERR_* */
|
||||
int32_t allow; /* CHECK:1=allow 0=block(block 由服务端策略引擎判定) */
|
||||
uint16_t text_len;
|
||||
uint16_t pad;
|
||||
char text[MEOW_IPC_MAX - 12];
|
||||
} m_net_sdk_result_t;
|
||||
|
||||
#endif /* MEOW_IPC_H */
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* meow_lazygrid.h — SDK 2 维虚拟化宫格(§12.4:meow_lazygrid,M7.4 主界面前置)。
|
||||
*
|
||||
* 定位:给「卡片/图标宫格」提供**二维虚拟化**——应用入口宫格、图标墙、设置快捷卡等。
|
||||
* 只实例化**可见行**(视口内),行高/列几何 + gap;滚动复用 §12.3 meow_scroll(fling/snap),
|
||||
* 与 meow_listview 同款惯性;命中 meow_lazygrid_hit/tap_at 把滚动+网格坐标映射回 item idx。
|
||||
*
|
||||
* 设计(仿 meow_listview 的 vsource 思想 + meow_scroll 滚动 + meow_box 列几何):
|
||||
* - 数据源:count_fn() 返回总项数(任意长),bind_fn(index, item) 仅当 item「进入视口」
|
||||
* (绑定到固定池槽)时调一次;回滚命中同槽不重绑(同 listview 池语义)。
|
||||
* - 固定 item 池:池容量 = 视口行数 + 2(× cols),静态有界(≤MEOW_LAZYGRID_POOL_MAX)。
|
||||
* - 列几何:cols + cell_w + cell_h + gap;行高 row_h = cell_h + gap;按 scroll offset 推可见行。
|
||||
* - 渲染:默认 renderer 画「圆角卡片(icon+名+状态)」;App 也可经 set_item_draw 完全自绘
|
||||
* (主界面卡片宫格用:icon+名+后台标注)。支持选中高亮/禁用变淡/空态占位。
|
||||
* - 滚动:接 meow_scroll(meow_lazygrid_scroll 返回会话;track/fling/snap 与 listview 同款)。
|
||||
*
|
||||
* 内存纪律(M7 §5.2):静态有界 item 池(冷路径 create/set_vsource 分配,热路径 draw/hit/scroll
|
||||
* 零 malloc),与 listview 池同源;不常驻 count 个 item 对象。
|
||||
*
|
||||
* 非 meow_widget:lazygrid 是**自包含控件**(不注册进 z 序/绘制注册表),由宿主(launcher session)
|
||||
* 在 draw 阶段调 meow_lazygrid_draw、命中阶段调 meow_lazygrid_hit/tap_at、滚动喂 meow_scroll——
|
||||
* 与 meow_scroll/meow_box 同为「正交模块」,不侵入 struct meow_widget(全追加式)。
|
||||
*/
|
||||
#ifndef MEOW_UI_LAZYGRID_H
|
||||
#define MEOW_UI_LAZYGRID_H
|
||||
|
||||
#include "meow_ui_types.h" /* meow_img 前向 + 基础类型(子头不引伞头,避免相对路径依赖) */
|
||||
#include "meow_ui_theme.h" /* meow_theme(draw 取语义色) */
|
||||
#include "meow_ui_scroll.h" /* meow_scroll(滚动集成;子头引依赖子头,同 widgets.h→theme.h) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct meow_lazygrid meow_lazygrid;
|
||||
|
||||
/* ---- cell / item ---- */
|
||||
/* 单格数据(bind 回调填充;SDK 固定池持有;App 跨 bind 用 user 存业务态/图/index)。 */
|
||||
typedef struct meow_lazygrid_item {
|
||||
const char *title; /* 主文本(名;必填,NULL 视为空格) */
|
||||
const char *subtitle; /* 副文本(状态/后台标注),可空 */
|
||||
const meow_img *icon; /* 可选:格内图标(NULL=无) */
|
||||
int enabled; /* 1=可交互;0=变淡不响应 */
|
||||
void *user; /* App 私有(跨 bind 保存业务态;SDK 不碰) */
|
||||
} meow_lazygrid_item;
|
||||
|
||||
/* 数据源:count 返回总项数(>=0,任意长);bind 在 item 进入视口时调一次填充 item。
|
||||
* 二者均不常驻 item 对象;count 每帧实时查(支持动态长度/搜索过滤)。 */
|
||||
typedef int (*meow_lazygrid_count_fn)(void *ctx);
|
||||
typedef void (*meow_lazygrid_bind_fn)(int index, meow_lazygrid_item *item, void *ctx);
|
||||
|
||||
/* 完全自绘回调(覆盖默认 renderer):index=项号;x,y,w,h=格矩形(已裁剪到宫格视口);
|
||||
* state=MEOW_LAZYGRID_ST_* 状态位(选中/禁用组合)。App 用 §3 绘制 API 自绘整格。 */
|
||||
typedef void (*meow_lazygrid_draw_fn)(int index, int x, int y, int w, int h,
|
||||
int state, void *ctx);
|
||||
/* state 位(draw 回调收到)。 */
|
||||
#define MEOW_LAZYGRID_ST_SELECTED (1u<<0)
|
||||
#define MEOW_LAZYGRID_ST_DISABLED (1u<<1)
|
||||
|
||||
/* 命中/点击回调(idx=项号;App 在 tap_at 命中后触发动作)。 */
|
||||
typedef void (*meow_lazygrid_tap_fn)(int idx, void *ctx);
|
||||
|
||||
#define MEOW_LAZYGRID_POOL_MAX 96 /* 池容量上限(固定;保证静态有界) */
|
||||
|
||||
/* ---- 生命周期(冷路径 create/free;热路径 draw/hit/scroll 零 malloc)----
|
||||
* cols=列数(>=1,越界 clamp);cell_w/cell_h=格宽高;gap=格间距(水平与垂直同用)。 */
|
||||
meow_lazygrid *meow_lazygrid_create(int x, int y, int w, int h, int cols,
|
||||
int cell_w, int cell_h, int gap);
|
||||
void meow_lazygrid_free(meow_lazygrid *g);
|
||||
|
||||
/* ---- 几何配置 ---- */
|
||||
void meow_lazygrid_set_rect(meow_lazygrid *g, int x, int y, int w, int h); /* 视口矩形(绝对坐标) */
|
||||
void meow_lazygrid_set_cols(meow_lazygrid *g, int cols);
|
||||
void meow_lazygrid_set_cell(meow_lazygrid *g, int cell_w, int cell_h, int gap);
|
||||
|
||||
/* ---- 数据源 + 渲染 ---- */
|
||||
int meow_lazygrid_set_vsource(meow_lazygrid *g, meow_lazygrid_count_fn count,
|
||||
meow_lazygrid_bind_fn bind, void *ctx);
|
||||
int meow_lazygrid_set_item_draw(meow_lazygrid *g, meow_lazygrid_draw_fn draw,
|
||||
void *ctx); /* NULL=用默认卡片 renderer */
|
||||
int meow_lazygrid_set_empty_text(meow_lazygrid *g, const char *text);
|
||||
|
||||
/* ---- 滚动(像素级;可视行数按 offset 实时推) ---- */
|
||||
void meow_lazygrid_set_scroll(meow_lazygrid *g, int off);
|
||||
void meow_lazygrid_scroll_by(meow_lazygrid *g, int dy);
|
||||
int meow_lazygrid_scroll(meow_lazygrid *g);
|
||||
int meow_lazygrid_max_scroll(meow_lazygrid *g); /* max(0, 内容高 - 视口高) */
|
||||
int meow_lazygrid_row_h(const meow_lazygrid *g); /* 行高(=cell_h+gap) */
|
||||
/* 便捷:包一个 meow_scroll 会话(set/max/get/redraw 指回本宫格;redraw 只刷视口区)。
|
||||
* App 在 on_touch_move 喂 meow_scroll_track、松手 meow_scroll_fling。返回 NULL=池满。 */
|
||||
meow_scroll *meow_lazygrid_scroll_attach(meow_lazygrid *g);
|
||||
|
||||
/* ---- 选中 / 命中 ---- */
|
||||
void meow_lazygrid_set_on_tap(meow_lazygrid *g, meow_lazygrid_tap_fn cb, void *ctx);
|
||||
void meow_lazygrid_set_sel(meow_lazygrid *g, int idx); /* 越界 clamp;<0 清除;标脏 */
|
||||
int meow_lazygrid_sel(const meow_lazygrid *g);
|
||||
int meow_lazygrid_hit(meow_lazygrid *g, int x, int y); /* 命中项 idx(滚动+网格坐标映射);-1=未命中/禁用 */
|
||||
int meow_lazygrid_tap_at(meow_lazygrid *g, int x, int y); /* 命中:置选中 + 回调 + 返回 idx/-1 */
|
||||
|
||||
/* ---- 绘制 ---- */
|
||||
void meow_lazygrid_draw(meow_lazygrid *g, const meow_theme *th);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* MEOW_UI_LAZYGRID_H */
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* meow_select.h — SDK 枚举/预设选择器(§5.8.6 模态组件,M7.2 设置页缺口 #4)。
|
||||
*
|
||||
* 定位:给「枚举 / 预设选择器」一个开箱即用的**下拉模态**——设置页采样率/码率/主题/语言等
|
||||
* 枚举、均衡器预设等。封装 = BottomSheet(标题 + 选项列表)+ meow_listview(SINGLE) 单选列表:
|
||||
* 选项数组 + 当前选中(高亮)+ 点选项 = 选中 + 关闭 + on_change 回调;点面板外/左缘返回 = 取消。
|
||||
*
|
||||
* 设计(§3 缺口 #4):选项用 meow_listview(SINGLE) 渲染(单选高亮、可惯性滚动、虚拟化长列表),
|
||||
* 外层是模态 BottomSheet(复用 meow_dialog 的遮罩/快照恢复,scrim 只叠一层/关闭无残影)。
|
||||
* 阻塞式(内层 meow_input_pop 轮询,与 meow_dialog 同构);打开前排空残留触摸。
|
||||
*
|
||||
* 内存纪律:选项数组定长(≤MEOW_SELECT_MAX_ITEMS,栈/数组持有,非 malloc 长列表);
|
||||
* 列表在 open 时创建(冷路径)、close 时释放;open 循环零 malloc。
|
||||
*/
|
||||
#ifndef MEOW_UI_SELECT_H
|
||||
#define MEOW_UI_SELECT_H
|
||||
|
||||
#include "meow_ui_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct meow_select meow_select;
|
||||
|
||||
/* 选中回调(on_change):idx=被选中的选项下标(0..n-1)。 */
|
||||
typedef void (*meow_select_fn)(int idx, void *ctx);
|
||||
|
||||
#define MEOW_SELECT_MAX_ITEMS 32 /* 选项上限(静态数组;越界 add 忽略并返回 -1) */
|
||||
|
||||
/* ---- 组件式(可复用 + on_change 回调)---- */
|
||||
meow_select *meow_select_create(const char *title, int cur); /* cur=初始选中项;越界 clamp */
|
||||
int meow_select_add_item(meow_select *s, const char *label); /* 返回项下标;满/无效= -1 */
|
||||
void meow_select_set_on_change(meow_select *s, meow_select_fn cb, void *ctx);
|
||||
/* 打开模态(阻塞到确认/取消):选中 → 置 result=idx + 调 on_change(idx)+ 返回 idx;
|
||||
* 取消/点面板外/左缘返回 → 返回 -1(不调 on_change)。 */
|
||||
int meow_select_open(meow_select *s);
|
||||
void meow_select_free(meow_select *s);
|
||||
|
||||
/* ---- 一次性(无回调,返回值即选择)----
|
||||
* opts=选项数组(const char* 数组);n=个数;cur=初始选中。返回选中下标/-1=取消。 */
|
||||
int meow_select_show(const char *title, const char *const *opts, int n, int cur);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* MEOW_UI_SELECT_H */
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* meow_ui_net.h — MeowOS SDK 网络自愿通道(NETMGR_DESIGN §6.3/§4;追加式 API,含入 meow_ui.h)。
|
||||
*
|
||||
* 定位:框架 App 联网前的**自愿自治**通道 + **自愿字节上报**,二者都不强制(防君子不防小人)。
|
||||
* - meow_net_policy_check(host):联网前自查 host 是否被 netmgrd 管控策略放行。
|
||||
* 返回 0=allow(可联网);1=block(策略拒绝,App 应中止本次联网)。
|
||||
* netmgrd 不在 / 超时 → 0(乐观放行,绝不因监控不可用而误封业务)。
|
||||
* - meow_net_report(rx,tx):App 自愿上报本进程累计收发字节。内核无 per-socket/per-uid
|
||||
* 字节计数器(NETMGR_DESIGN §4 表),§4 的「字节级精确记账走 SDK 自报通道」由此落地;
|
||||
* netmgrd 记入 per-实体统计(status JSON `entity_bytes`)。best-effort,失败静默不影响业务。
|
||||
* 用法:App 在发起 HTTP/TLS 前调 meow_net_policy_check(host),若返回 1 则按产品语义处理(如提示被限)。
|
||||
*/
|
||||
#ifndef MEOW_UI_NET_H
|
||||
#define MEOW_UI_NET_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* 检查 host 是否被策略放行。0=allow;1=block;netmgrd 不在/超时 → 0(乐观放行)。
|
||||
* host 为域名(如 "music.163.com");判定以当前 App(MEOW_LOG_APP)为实体,服务端策略引擎权威执行。 */
|
||||
int meow_net_policy_check(const char *host);
|
||||
|
||||
/* 上报本进程累计收发字节(best-effort,失败静默)。 */
|
||||
void meow_net_report(uint32_t rx, uint32_t tx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* MEOW_UI_NET_H */
|
||||
@@ -0,0 +1,302 @@
|
||||
/* sdk/meow_cachegov.c — 磁盘缓存 page-cache 治理 SDK 库(A:DONTNEED 主导侧)。
|
||||
*
|
||||
* 权威方案:devdocs/plan/OOM_PAGECACHE_GOVERNANCE.md。归属(ARCHITECTURE_PRINCIPLES ③混合):
|
||||
* A 热路径(register/tick/drop_finished/refault)随 SDK 库;C(/proc/meminfo Cached 监控)
|
||||
* 归 meow_svc_perf,本库只读其共享状态文件 MEOW_CACHEGOV_C_FILE 作为降载信号。
|
||||
*
|
||||
* 红线(务必保留本注释):**L2 禁止**——绝不丢 `[read_pos, ...)` 任何未解码区(含 read_pos 前方
|
||||
* 刚写入尚未解码的页)。本库所有 drop 的上界只能是 read_pos-keep_hot(L1)或整文件曲终(L0)。
|
||||
*
|
||||
* 分层:只依赖 pthread + meow_log + POSIX(posix_fadvise),不依赖 core/App 其它 TU。
|
||||
* 自含:refault 滚窗 + 三级回退(放大 keep_hot → 降 L0 → 关 A)。
|
||||
*/
|
||||
#include "include/meow_cachegov.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* meow_ui.h:SDK 公共伞头(含 meow_log/meow_ui_log.h + 公共类型)。 */
|
||||
#include "meow_ui.h"
|
||||
/* 共享协议头:C 侧状态文件路径(meow_svc_perf 写入 / 各 App 读取,同源防漂移)。 */
|
||||
#include "meow_ipc.h"
|
||||
|
||||
#define MEOW_CACHEGOV_MAXREG 8 /* 同时治理的缓存文件上限(ncm 单点播放,远小于此) */
|
||||
#define MEOW_CACHEGOV_REFAULTRING 64 /* refault 滚窗环形缓冲 */
|
||||
#define MEOW_CACHEGOV_WINDOW_MS 10000 /* refault 滚窗 10s */
|
||||
#define MEOW_CACHEGOV_HOTESCALATE 1 /* 回退档>=1 时 keep_hot 放大倍数(翻倍) */
|
||||
#define MEOW_CACHEGOV_DROP_THROTTLE (64u * 1024u) /* L1 每次至少推进 64KB 才再 drop(省 open/fadvise) */
|
||||
|
||||
typedef struct cg_entry {
|
||||
char path[512];
|
||||
uint64_t size; /* 标称大小(流式=nominal;命中=实际) */
|
||||
uint64_t (*read_pos_fn)(void *ctx);
|
||||
void *ctx;
|
||||
uint64_t dropped_to; /* 已 DONTNEED 的已读前缀上界(只推进,不回退) */
|
||||
int used;
|
||||
} cg_entry_t;
|
||||
|
||||
static pthread_mutex_t g_mu = PTHREAD_MUTEX_INITIALIZER;
|
||||
static struct meow_cachegov_cfg g_cfg = {
|
||||
.enabled = 1,
|
||||
.cached_threshold_kb = 0, /* C 归属 svc_perf,本库只读 */
|
||||
.dirty_warn_kb = 0, /* 同上 */
|
||||
.keep_hot_bytes = (256u * 1024u), /* 保 256KB 解码窗口供倒带 */
|
||||
.min_hot_ahead = (0), /* L2 红线防御值;绝不参与 drop */
|
||||
.drop_mode = 0, /* 默认仅曲终(L0);L1 需显式开(带 seek 回退) */
|
||||
.refault_backoff = 3, /* 10s 内 3 次 refault → 回退 */
|
||||
};
|
||||
static cg_entry_t g_reg[MEOW_CACHEGOV_MAXREG];
|
||||
static int g_backoff = 0; /* 0=正常 1=keep_hot放大 2=降L0 3=A关 */
|
||||
static int64_t g_last_step_ms = -1000000000000LL; /* 上次升/降档时刻(初始很远 → 首次可动作) */
|
||||
static int64_t g_refault_ring[MEOW_CACHEGOV_REFAULTRING];
|
||||
static int g_refault_head = 0;
|
||||
static int g_refault_n = 0;
|
||||
|
||||
/* ---- 时钟(宿主测试桩可注入) ---- */
|
||||
static int64_t g_test_now_ms = -1;
|
||||
static int64_t now_ms(void)
|
||||
{
|
||||
if (g_test_now_ms >= 0) return g_test_now_ms;
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return (int64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
|
||||
}
|
||||
void meow_cachegov_test_set_time_ms(int64_t t) { g_test_now_ms = t; }
|
||||
|
||||
/* ---- C 侧状态文件(svc_perf 写 / 本库读) ---- */
|
||||
static const char *c_file(void)
|
||||
{
|
||||
const char *e = getenv("MEOW_CACHEGOV_C_FILE");
|
||||
return (e && e[0]) ? e : MEOW_CACHEGOV_C_FILE;
|
||||
}
|
||||
|
||||
/* C 状态(svc_perf 写)最近一次解析缓存:下载 cb 每 4KB 块会查一次,避免每块都读文件(200ms TTL)。 */
|
||||
static int g_cached_c_dl = 0, g_cached_c_pf = 0;
|
||||
static int64_t g_cached_c_at = 0;
|
||||
|
||||
/* 读 C 状态文件(缺省 0=不降载)。解析失败/文件不存在 → 全部 0。 */
|
||||
static void read_c_state(int *dl_pause, int *pf_pause, long *cached_kb, long *dirty_kb)
|
||||
{
|
||||
if (dl_pause) *dl_pause = 0;
|
||||
if (pf_pause) *pf_pause = 0;
|
||||
if (cached_kb) *cached_kb = 0;
|
||||
if (dirty_kb) *dirty_kb = 0;
|
||||
FILE *f = fopen(c_file(), "r");
|
||||
if (!f) return;
|
||||
char line[160];
|
||||
while (fgets(line, sizeof line, f)) {
|
||||
if (!strncmp(line, "cached_kb=", 10)) { if (cached_kb) *cached_kb = atol(line + 10); }
|
||||
else if (!strncmp(line, "dirty_kb=", 9)) { if (dirty_kb) *dirty_kb = atol(line + 9); }
|
||||
else if (!strncmp(line, "dl_pause=", 9)) { if (dl_pause) *dl_pause = atol(line + 9) ? 1 : 0; }
|
||||
else if (!strncmp(line, "prefetch_pause=", 15)) { if (pf_pause) *pf_pause = atol(line + 15) ? 1 : 0; }
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
/* ---- refault 滚窗 ---- */
|
||||
void meow_cachegov_note_refault(void)
|
||||
{
|
||||
int64_t t = now_ms();
|
||||
pthread_mutex_lock(&g_mu);
|
||||
g_refault_ring[g_refault_head] = t;
|
||||
g_refault_head = (g_refault_head + 1) % MEOW_CACHEGOV_REFAULTRING;
|
||||
if (g_refault_n < MEOW_CACHEGOV_REFAULTRING) g_refault_n++;
|
||||
pthread_mutex_unlock(&g_mu);
|
||||
}
|
||||
|
||||
static int refaults_last_window(void)
|
||||
{
|
||||
int64_t now = now_ms();
|
||||
int cnt = 0, start = (g_refault_head - g_refault_n + MEOW_CACHEGOV_REFAULTRING) %
|
||||
MEOW_CACHEGOV_REFAULTRING;
|
||||
for (int i = 0; i < g_refault_n; i++) {
|
||||
int64_t t = g_refault_ring[(start + i) % MEOW_CACHEGOV_REFAULTRING];
|
||||
if (t >= now - MEOW_CACHEGOV_WINDOW_MS) cnt++;
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/* 曲终/cur_finished 后:整文件 L0 DONTNEED。 */
|
||||
int meow_cachegov_drop_finished(const char *path)
|
||||
{
|
||||
if (!path) return -1;
|
||||
pthread_mutex_lock(&g_mu);
|
||||
for (int i = 0; i < MEOW_CACHEGOV_MAXREG; i++) {
|
||||
if (g_reg[i].used && strcmp(g_reg[i].path, path) == 0) {
|
||||
g_reg[i].used = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
int lbf = g_backoff, en = g_cfg.enabled;
|
||||
pthread_mutex_unlock(&g_mu);
|
||||
|
||||
if (!en || lbf >= 3) { /* A 已整体关闭:连曲终 L0 也不丢(§2.3 步骤 3) */
|
||||
meow_log(MEOW_LOG_INFO, "[cachegov] drop_finished skip %s (A off, backoff=%d)", path, lbf);
|
||||
return 0;
|
||||
}
|
||||
int fd = open(path, O_RDONLY);
|
||||
if (fd < 0) return -1;
|
||||
/* len=0 → 到 EOF(整文件)。曲终播放器已不再读该文件,重播=冷缓存首读(等价未命中)。 */
|
||||
posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED);
|
||||
close(fd);
|
||||
meow_log(MEOW_LOG_INFO, "[cachegov] drop_finished L0 %s", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* L1:丢已读前缀 [dropped_to, read_pos-keep_hot)。返回 1=有丢。 */
|
||||
static int entry_drop_l1(cg_entry_t *e, size_t keep)
|
||||
{
|
||||
if (!e->read_pos_fn) return 0;
|
||||
uint64_t pos = e->read_pos_fn(e->ctx);
|
||||
if (pos <= keep) return 0; /* keep_hot 覆盖整个已读前缀,无需丢 */
|
||||
uint64_t drop_end = pos - (uint64_t)keep; /* L1 上界:read_pos 前方绝不丢(L2 红线) */
|
||||
if (drop_end <= e->dropped_to) return 0; /* 无推进(seek 回退 / 前进不足) */
|
||||
if (drop_end - e->dropped_to < MEOW_CACHEGOV_DROP_THROTTLE && e->dropped_to != 0)
|
||||
return 0; /* 节流:不足 64KB 不重复 open */
|
||||
int fd = open(e->path, O_RDONLY);
|
||||
if (fd < 0) return 0;
|
||||
off_t off = (off_t)e->dropped_to;
|
||||
off_t len = (off_t)(drop_end - e->dropped_to);
|
||||
ssize_t r = posix_fadvise(fd, off, len, POSIX_FADV_DONTNEED);
|
||||
close(fd);
|
||||
if (r != 0) return 0;
|
||||
e->dropped_to = drop_end;
|
||||
meow_log(MEOW_LOG_INFO, "[cachegov] L1 drop %s off=%lld len=%lld (pos=%lld keep=%zu)",
|
||||
e->path, (long long)off, (long long)len, (long long)pos, keep);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int meow_cachegov_tick(void)
|
||||
{
|
||||
pthread_mutex_lock(&g_mu);
|
||||
int64_t now = now_ms();
|
||||
/* ① 回退评估(滚窗 10s):refault 超门限 → 升一档;滚窗干净 ≥10s → 降一档。
|
||||
* 每升降一档需间隔 ≥10s(逐级回退,不因窗内持续 refault 一次跳过多档,防误回退)。 */
|
||||
int cnt = refaults_last_window();
|
||||
if (cnt >= g_cfg.refault_backoff && g_cfg.refault_backoff > 0 && g_backoff < 3 &&
|
||||
(now - g_last_step_ms) >= MEOW_CACHEGOV_WINDOW_MS) {
|
||||
g_backoff = g_backoff + 1;
|
||||
g_last_step_ms = now;
|
||||
meow_log(MEOW_LOG_INFO, "[cachegov] backoff -> %d (refault=%d/%lds)",
|
||||
g_backoff, cnt, MEOW_CACHEGOV_WINDOW_MS / 1000);
|
||||
} else if (cnt == 0 && g_backoff > 0 &&
|
||||
(now - g_last_step_ms) >= MEOW_CACHEGOV_WINDOW_MS) {
|
||||
g_backoff = g_backoff - 1;
|
||||
g_last_step_ms = now;
|
||||
meow_log(MEOW_LOG_INFO, "[cachegov] backoff -> %d (clean window)", g_backoff);
|
||||
}
|
||||
|
||||
size_t keep = g_cfg.keep_hot_bytes;
|
||||
if (g_backoff >= MEOW_CACHEGOV_HOTESCALATE) keep = g_cfg.keep_hot_bytes * 2; /* 档1:放大 keep_hot */
|
||||
int l1_on = g_cfg.enabled && g_cfg.drop_mode >= 1 && g_backoff < 2; /* 档2:L1 关 */
|
||||
int en = g_cfg.enabled;
|
||||
|
||||
/* ② L1 丢弃 */
|
||||
int dropped = 0;
|
||||
if (en && l1_on) {
|
||||
for (int i = 0; i < MEOW_CACHEGOV_MAXREG; i++)
|
||||
if (g_reg[i].used) dropped += entry_drop_l1(&g_reg[i], keep);
|
||||
}
|
||||
pthread_mutex_unlock(&g_mu);
|
||||
return dropped;
|
||||
}
|
||||
|
||||
/* ---- 注册 / 注销 ---- */
|
||||
int meow_cachegov_register(const char *path, uint64_t size,
|
||||
uint64_t (*read_pos_fn)(void *ctx), void *ctx)
|
||||
{
|
||||
if (!path) return -1;
|
||||
pthread_mutex_lock(&g_mu);
|
||||
int slot = -1;
|
||||
for (int i = 0; i < MEOW_CACHEGOV_MAXREG; i++) {
|
||||
if (g_reg[i].used && strcmp(g_reg[i].path, path) == 0) { slot = i; break; }
|
||||
if (!g_reg[i].used && slot < 0) slot = i;
|
||||
}
|
||||
if (slot < 0) { pthread_mutex_unlock(&g_mu); return -1; }
|
||||
snprintf(g_reg[slot].path, sizeof g_reg[slot].path, "%s", path);
|
||||
g_reg[slot].size = size;
|
||||
g_reg[slot].read_pos_fn = read_pos_fn;
|
||||
g_reg[slot].ctx = ctx;
|
||||
g_reg[slot].dropped_to = 0;
|
||||
g_reg[slot].used = 1;
|
||||
pthread_mutex_unlock(&g_mu);
|
||||
meow_log(MEOW_LOG_INFO, "[cachegov] register %s size=%llu", path, (unsigned long long)size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int meow_cachegov_unregister(const char *path)
|
||||
{
|
||||
if (!path) return -1;
|
||||
pthread_mutex_lock(&g_mu);
|
||||
int rc = -1;
|
||||
for (int i = 0; i < MEOW_CACHEGOV_MAXREG; i++)
|
||||
if (g_reg[i].used && strcmp(g_reg[i].path, path) == 0) { g_reg[i].used = 0; rc = 0; break; }
|
||||
pthread_mutex_unlock(&g_mu);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* ---- 降载查询(A 回退 OR svc_perf C) ---- */
|
||||
static void c_state(int *dl_pause, int *pf_pause)
|
||||
{
|
||||
int64_t now = now_ms();
|
||||
pthread_mutex_lock(&g_mu);
|
||||
if (g_cached_c_at == 0 || (now - g_cached_c_at) > 200) {
|
||||
read_c_state(&g_cached_c_dl, &g_cached_c_pf, NULL, NULL);
|
||||
g_cached_c_at = now;
|
||||
}
|
||||
if (dl_pause) *dl_pause = g_cached_c_dl;
|
||||
if (pf_pause) *pf_pause = g_cached_c_pf;
|
||||
pthread_mutex_unlock(&g_mu);
|
||||
}
|
||||
|
||||
int meow_cachegov_download_paused(void)
|
||||
{
|
||||
pthread_mutex_lock(&g_mu);
|
||||
int a = (g_backoff >= 2);
|
||||
pthread_mutex_unlock(&g_mu);
|
||||
int c = 0;
|
||||
c_state(&c, NULL);
|
||||
return (a || c) ? 1 : 0;
|
||||
}
|
||||
|
||||
int meow_cachegov_prefetch_paused(void)
|
||||
{
|
||||
pthread_mutex_lock(&g_mu);
|
||||
int a = (g_backoff >= 3);
|
||||
pthread_mutex_unlock(&g_mu);
|
||||
int c = 0;
|
||||
c_state(NULL, &c);
|
||||
return (a || c) ? 1 : 0;
|
||||
}
|
||||
|
||||
/* ---- 配置 ---- */
|
||||
int meow_cachegov_set_cfg(const struct meow_cachegov_cfg *cfg)
|
||||
{
|
||||
if (!cfg) return -1;
|
||||
pthread_mutex_lock(&g_mu);
|
||||
g_cfg = *cfg;
|
||||
pthread_mutex_unlock(&g_mu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int meow_cachegov_get_cfg(struct meow_cachegov_cfg *cfg)
|
||||
{
|
||||
if (!cfg) return -1;
|
||||
pthread_mutex_lock(&g_mu);
|
||||
*cfg = g_cfg;
|
||||
pthread_mutex_unlock(&g_mu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int meow_cachegov_backoff_level(void)
|
||||
{
|
||||
pthread_mutex_lock(&g_mu);
|
||||
int v = g_backoff;
|
||||
pthread_mutex_unlock(&g_mu);
|
||||
return v;
|
||||
}
|
||||
@@ -0,0 +1,350 @@
|
||||
/*
|
||||
* meow_lazygrid.c — §12.4 2 维虚拟化宫格(M7.4 主界面前置)。
|
||||
*
|
||||
* 自包含控件(非 meow_widget,不注册 z 序/绘制注册表):宿主在 draw 阶段调 meow_lazygrid_draw、
|
||||
* 命中阶段调 meow_lazygrid_hit/tap_at、滚动喂 meow_scroll(meow_lazygrid_scroll 返回会话)。
|
||||
* 只实例化**可见行**:固定 item 池(视口行数 + 2 × cols,静态有界 ≤MEOW_LAZYGRID_POOL_MAX),
|
||||
* bind 仅在 item 进入视口时调一次(回滚同槽不重绑,同 listview 池语义)。
|
||||
*
|
||||
* 内存纪律:池在 create/set_vsource/set_cell(冷路径)分配;draw/hit/set_scroll 热路径零 malloc。
|
||||
* 颜色全取 meow_theme_get() 语义色(日/夜/style 自动),不硬编码。
|
||||
*/
|
||||
#include "meow_ui.h"
|
||||
#include "include/meow_lazygrid.h"
|
||||
#include "meow_ui_internal.h" /* meow_gfx_clip_push/pop + meow_gfx_fill_round */
|
||||
#include "include/meow_ui_scroll.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct meow_lazygrid {
|
||||
int x, y, w, h; /* 视口矩形(绝对坐标) */
|
||||
int cols; /* 列数 */
|
||||
int cell_w, cell_h, gap; /* 格宽/格高/间距 */
|
||||
int row_h; /* 行高 = cell_h + gap(缓存) */
|
||||
|
||||
int scroll; /* 滚动偏移(像素) */
|
||||
int sel; /* 选中项(-1=无) */
|
||||
|
||||
meow_lazygrid_count_fn count_fn; void *vctx;
|
||||
meow_lazygrid_bind_fn bind;
|
||||
meow_lazygrid_draw_fn draw_cb; void *draw_ctx;
|
||||
meow_lazygrid_tap_fn tap_cb; void *tap_ctx;
|
||||
|
||||
/* 固定 item 池(冷路径分配;slot 按 item index mod pool_n 映射) */
|
||||
meow_lazygrid_item *pool;
|
||||
int *pool_idx; /* 每槽绑定的 item index(-1=空闲) */
|
||||
int pool_n;
|
||||
|
||||
char empty_text[64]; /* 空态占位(首字符 0=不画) */
|
||||
};
|
||||
|
||||
/* 行高(= cell_h + gap)。 */
|
||||
int meow_lazygrid_row_h(const meow_lazygrid *g)
|
||||
{ return g ? g->row_h : 0; }
|
||||
|
||||
/* 总行数。 */
|
||||
static int lg_rows(const meow_lazygrid *g)
|
||||
{
|
||||
int n = g->count_fn ? g->count_fn(g->vctx) : 0;
|
||||
if (n < 0) n = 0;
|
||||
if (n == 0) return 0;
|
||||
return (n + g->cols - 1) / g->cols;
|
||||
}
|
||||
/* 总项数。 */
|
||||
static int lg_count(const meow_lazygrid *g)
|
||||
{ return g->count_fn ? g->count_fn(g->vctx) : 0; }
|
||||
|
||||
/* 内容总高(行高 × 行数 − 末行后无 gap)。 */
|
||||
static int lg_content_h(const meow_lazygrid *g)
|
||||
{
|
||||
int rows = lg_rows(g);
|
||||
if (rows <= 0) return 0;
|
||||
return rows * g->cell_h + (rows - 1) * g->gap;
|
||||
}
|
||||
|
||||
/* ---- 生命周期 ---- */
|
||||
meow_lazygrid *meow_lazygrid_create(int x, int y, int w, int h, int cols,
|
||||
int cell_w, int cell_h, int gap)
|
||||
{
|
||||
if (cols < 1) cols = 1;
|
||||
meow_lazygrid *g = (meow_lazygrid *)calloc(1, sizeof *g);
|
||||
if (!g) return NULL;
|
||||
g->x = x; g->y = y; g->w = w; g->h = h; g->cols = cols;
|
||||
g->cell_w = cell_w > 0 ? cell_w : 1;
|
||||
g->cell_h = cell_h > 0 ? cell_h : 1;
|
||||
g->gap = gap > 0 ? gap : 0;
|
||||
g->row_h = g->cell_h + g->gap;
|
||||
g->sel = -1;
|
||||
return g;
|
||||
}
|
||||
|
||||
void meow_lazygrid_free(meow_lazygrid *g)
|
||||
{
|
||||
if (!g) return;
|
||||
free(g->pool); free(g->pool_idx);
|
||||
free(g);
|
||||
}
|
||||
|
||||
void meow_lazygrid_set_rect(meow_lazygrid *g, int x, int y, int w, int h)
|
||||
{
|
||||
if (!g) return;
|
||||
g->x = x; g->y = y; g->w = w; g->h = h;
|
||||
}
|
||||
void meow_lazygrid_set_cols(meow_lazygrid *g, int cols)
|
||||
{
|
||||
if (!g) return;
|
||||
if (cols < 1) cols = 1;
|
||||
if (g->cols == cols) return;
|
||||
g->cols = cols;
|
||||
}
|
||||
void meow_lazygrid_set_cell(meow_lazygrid *g, int cell_w, int cell_h, int gap)
|
||||
{
|
||||
if (!g) return;
|
||||
g->cell_w = cell_w > 0 ? cell_w : 1;
|
||||
g->cell_h = cell_h > 0 ? cell_h : 1;
|
||||
g->gap = gap > 0 ? gap : 0;
|
||||
g->row_h = g->cell_h + g->gap; /* 行高变化 → 可见行数变 → 池容量随之调整 */
|
||||
}
|
||||
|
||||
/* ---- item 池(冷路径;有界 ≤MEOW_LAZYGRID_POOL_MAX)---- */
|
||||
static int lg_pool_cap(const meow_lazygrid *g)
|
||||
{
|
||||
int rh = g->row_h; if (rh < 8) rh = 8;
|
||||
int rows = (g->h / rh) + 1; if (rows < 1) rows = 1;
|
||||
int cap = (rows + 2) * g->cols;
|
||||
if (cap < 8) cap = 8;
|
||||
if (cap > MEOW_LAZYGRID_POOL_MAX) cap = MEOW_LAZYGRID_POOL_MAX;
|
||||
return cap;
|
||||
}
|
||||
/* 确保池容量 ≥ cap(有界扩张;重分配清空绑定 → 下一帧按需重绑)。返回 0=成功;-1=分配失败。 */
|
||||
static int lg_ensure_pool(meow_lazygrid *g)
|
||||
{
|
||||
if (!g->count_fn || !g->bind) return 0;
|
||||
int cap = lg_pool_cap(g);
|
||||
if (g->pool && g->pool_n >= cap) return 0;
|
||||
meow_lazygrid_item *np = (meow_lazygrid_item *)calloc((size_t)cap, sizeof *np);
|
||||
int *ni = (int *)calloc((size_t)cap, sizeof *ni);
|
||||
if (!np || !ni) { free(np); free(ni); return -1; }
|
||||
for (int i = 0; i < cap; i++) ni[i] = -1;
|
||||
free(g->pool); free(g->pool_idx);
|
||||
g->pool = np; g->pool_idx = ni; g->pool_n = cap;
|
||||
return 0;
|
||||
}
|
||||
/* 取 index 对应的池槽(按 item index mod pool_n 映射;槽绑定不匹配 → 防御性重绑)。 */
|
||||
static meow_lazygrid_item *lg_slot(meow_lazygrid *g, int index)
|
||||
{
|
||||
if (!g->pool || g->pool_n <= 0) return NULL;
|
||||
int s = (int)((unsigned)index % (unsigned)g->pool_n);
|
||||
if (g->pool_idx[s] != index) {
|
||||
memset(&g->pool[s], 0, sizeof g->pool[s]);
|
||||
g->bind(index, &g->pool[s], g->vctx);
|
||||
g->pool_idx[s] = index;
|
||||
}
|
||||
return &g->pool[s];
|
||||
}
|
||||
|
||||
/* ---- 数据源 + 渲染 ---- */
|
||||
int meow_lazygrid_set_vsource(meow_lazygrid *g, meow_lazygrid_count_fn count,
|
||||
meow_lazygrid_bind_fn bind, void *ctx)
|
||||
{
|
||||
if (!g || !count || !bind) return -1;
|
||||
g->count_fn = count; g->bind = bind; g->vctx = ctx;
|
||||
if (lg_ensure_pool(g) != 0) { /* 分配固定 item 池(有界,冷路径) */
|
||||
g->count_fn = NULL; g->bind = NULL; /* 池分配失败:回退为空 */
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int meow_lazygrid_set_item_draw(meow_lazygrid *g, meow_lazygrid_draw_fn draw,
|
||||
void *ctx)
|
||||
{
|
||||
if (!g) return -1;
|
||||
g->draw_cb = draw; g->draw_ctx = ctx; /* NULL=用默认卡片 renderer */
|
||||
return 0;
|
||||
}
|
||||
int meow_lazygrid_set_empty_text(meow_lazygrid *g, const char *text)
|
||||
{
|
||||
if (!g) return -1;
|
||||
if (text) {
|
||||
strncpy(g->empty_text, text, sizeof g->empty_text - 1);
|
||||
g->empty_text[sizeof g->empty_text - 1] = '\0';
|
||||
} else {
|
||||
g->empty_text[0] = '\0';
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---- 滚动 ---- */
|
||||
int meow_lazygrid_max_scroll(meow_lazygrid *g)
|
||||
{
|
||||
if (!g) return 0;
|
||||
int ch = lg_content_h(g);
|
||||
int ms = ch - g->h;
|
||||
return ms > 0 ? ms : 0;
|
||||
}
|
||||
void meow_lazygrid_set_scroll(meow_lazygrid *g, int off)
|
||||
{
|
||||
if (!g) return;
|
||||
int ms = meow_lazygrid_max_scroll(g);
|
||||
if (off < 0) off = 0;
|
||||
if (off > ms) off = ms;
|
||||
g->scroll = off;
|
||||
}
|
||||
void meow_lazygrid_scroll_by(meow_lazygrid *g, int dy)
|
||||
{ if (g) meow_lazygrid_set_scroll(g, g->scroll + dy); }
|
||||
int meow_lazygrid_scroll(meow_lazygrid *g) { return g ? g->scroll : 0; }
|
||||
|
||||
/* ---- meow_scroll 集成(callbacks 指回本宫格)---- */
|
||||
static void lg_s_set(void *c, int off) { meow_lazygrid_set_scroll((meow_lazygrid *)c, off); }
|
||||
static int lg_s_max(void *c) { return meow_lazygrid_max_scroll((meow_lazygrid *)c); }
|
||||
static int lg_s_get(void *c) { return meow_lazygrid_scroll((meow_lazygrid *)c); }
|
||||
static void lg_s_redraw(void *c)
|
||||
{
|
||||
meow_lazygrid *g = (meow_lazygrid *)c;
|
||||
if (g && g->w > 0 && g->h > 0) {
|
||||
meow_lazygrid_draw(g, meow_theme_get());
|
||||
meow_ui_flush_region(g->x, g->y, g->w, g->h); /* 只刷视口区 */
|
||||
}
|
||||
}
|
||||
meow_scroll *meow_lazygrid_scroll_attach(meow_lazygrid *g)
|
||||
{
|
||||
if (!g) return NULL;
|
||||
return meow_scroll_create(lg_s_set, lg_s_max, lg_s_get, lg_s_redraw, g);
|
||||
}
|
||||
|
||||
/* ---- 选中 ---- */
|
||||
void meow_lazygrid_set_sel(meow_lazygrid *g, int idx)
|
||||
{
|
||||
if (!g) return;
|
||||
int n = lg_count(g);
|
||||
if (idx < 0) { g->sel = -1; return; }
|
||||
if (n > 0 && idx >= n) idx = n - 1;
|
||||
g->sel = idx;
|
||||
}
|
||||
int meow_lazygrid_sel(const meow_lazygrid *g) { return g ? g->sel : -1; }
|
||||
void meow_lazygrid_set_on_tap(meow_lazygrid *g, meow_lazygrid_tap_fn cb, void *ctx)
|
||||
{
|
||||
if (!g) return;
|
||||
g->tap_cb = cb; g->tap_ctx = ctx;
|
||||
}
|
||||
int meow_lazygrid_tap_at(meow_lazygrid *g, int x, int y)
|
||||
{
|
||||
int idx = meow_lazygrid_hit(g, x, y);
|
||||
if (idx < 0) return -1;
|
||||
meow_lazygrid_set_sel(g, idx);
|
||||
if (g->tap_cb) g->tap_cb(idx, g->tap_ctx);
|
||||
return idx;
|
||||
}
|
||||
|
||||
/* ---- 命中(滚动 + 网格坐标 → item idx)----
|
||||
* 点在格内(跨过 gap 区域返回 -1);禁用格不响应(同 listview enabled=0 语义)。 */
|
||||
int meow_lazygrid_hit(meow_lazygrid *g, int x, int y)
|
||||
{
|
||||
if (!g) return -1;
|
||||
if (x < g->x || x >= g->x + g->w || y < g->y || y >= g->y + g->h) return -1;
|
||||
/* 列格(水平):col = px / (cell_w+gap),落点在 gap 内 → 未命中。 */
|
||||
int px = x - g->x;
|
||||
int cstep = g->cell_w + g->gap;
|
||||
int col = px / cstep;
|
||||
if (col < 0 || col >= g->cols) return -1;
|
||||
if ((px - col * cstep) >= g->cell_w) return -1;
|
||||
/* 行格(垂直,含滚动):row = (py+scroll)/row_h;落点在 gap 内 → 未命中。 */
|
||||
int py = y - g->y + g->scroll;
|
||||
int rstep = g->row_h;
|
||||
int row = py / rstep;
|
||||
if (row < 0) return -1;
|
||||
if ((py - row * rstep) >= g->cell_h) return -1;
|
||||
int idx = row * g->cols + col;
|
||||
int n = lg_count(g);
|
||||
if (idx < 0 || idx >= n) return -1;
|
||||
/* 禁用格不响应(顺手 bind 临时 enabled 查:与 listview hit 同法)。 */
|
||||
if (g->bind) {
|
||||
meow_lazygrid_item it;
|
||||
memset(&it, 0, sizeof it);
|
||||
g->bind(idx, &it, g->vctx);
|
||||
if (!it.enabled) return -1;
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
/* ---- 绘制(只画可见行;clip 钳到宫格视口)---- */
|
||||
static void lg_draw_cell(meow_lazygrid *g, const meow_theme *th, int index,
|
||||
int cell_x, int cell_y, meow_lazygrid_item *it)
|
||||
{
|
||||
int sel = (index == g->sel);
|
||||
int txtcol = sel ? th->onPrimaryContainer : th->onSurface;
|
||||
int subcol = sel ? th->onPrimaryContainer : th->onSurfaceVariant;
|
||||
uint32_t bg = sel ? th->primaryContainer : th->surfaceContainerLow;
|
||||
meow_gfx_fill_round(cell_x, cell_y, g->cell_w, g->cell_h, th->radius_card, bg);
|
||||
|
||||
int pad = 8;
|
||||
int thh = meow_ui_text_height(th->font_body);
|
||||
int chh = g->cell_h, cw = g->cell_w;
|
||||
/* 图标(可选):居中于格顶 ~50%。 */
|
||||
if (it->icon) {
|
||||
int isz = cw * 6 / 10; if (isz > chh * 5 / 10) isz = chh * 5 / 10;
|
||||
if (isz < 16) isz = 16;
|
||||
int icx = cell_x + cw / 2, icy = cell_y + pad + isz / 2;
|
||||
meow_ui_img_scaled(icx - isz / 2, icy - isz / 2, isz, isz, (meow_img *)it->icon);
|
||||
}
|
||||
/* 标题 + 副标题(主界面:名 + 状态)。底部对齐。 */
|
||||
int has_sub = (it->subtitle && it->subtitle[0]);
|
||||
int ty = cell_y + chh - pad - thh - (has_sub ? (thh + 2) : 0);
|
||||
if (it->title) meow_ui_text_center(cell_x + cw / 2, ty, it->title, th->font_body, txtcol);
|
||||
if (has_sub) meow_ui_text_center(cell_x + cw / 2, ty + thh + 2, it->subtitle, th->font_body, subcol);
|
||||
|
||||
/* 选中描边(轮廓);禁用整体变淡。 */
|
||||
if (sel) meow_ui_draw_rect_round(cell_x, cell_y, cw, chh, th->radius_card, th->primary);
|
||||
if (!it->enabled)
|
||||
meow_ui_fill_rect_alpha(cell_x, cell_y, cw, chh, th->onSurface, 90);
|
||||
}
|
||||
|
||||
void meow_lazygrid_draw(meow_lazygrid *g, const meow_theme *th)
|
||||
{
|
||||
if (!g || !th) return;
|
||||
meow_gfx_clip_push(g->x, g->y, g->w, g->h); /* 格画不出宫格视口(防泄漏) */
|
||||
|
||||
int n = lg_count(g);
|
||||
int rows = lg_rows(g);
|
||||
lg_ensure_pool(g); /* 防御:视口/列几何变化时确保池容量 */
|
||||
|
||||
/* 空态占位:count==0 且设 empty_text → 视口内居中。 */
|
||||
if (n == 0 && g->empty_text[0]) {
|
||||
int thh = meow_ui_text_height(th->font_body);
|
||||
meow_ui_text_center(g->x + g->w / 2, g->y + (g->h - thh) / 2, g->empty_text,
|
||||
th->font_body, th->onSurfaceVariant);
|
||||
meow_gfx_clip_pop();
|
||||
return;
|
||||
}
|
||||
|
||||
int off = g->scroll;
|
||||
int cstep = g->cell_w + g->gap;
|
||||
int rstep = g->row_h;
|
||||
if (rstep <= 0) rstep = 1;
|
||||
int first = off / rstep;
|
||||
int last = (off + g->h) / rstep + 1; if (last > rows) last = rows;
|
||||
if (first < 0) first = 0;
|
||||
if (last > rows) last = rows;
|
||||
for (int r = first; r < last; r++) {
|
||||
int ry = g->y + (r * rstep - off); /* 本行格顶视口坐标 */
|
||||
for (int c = 0; c < g->cols; c++) {
|
||||
int index = r * g->cols + c;
|
||||
if (index >= n) break; /* 末行余下空格 */
|
||||
int cx = g->x + c * cstep;
|
||||
/* 行完全滚出视口(裁剪由 clip 兜底,这里跳过整行) */
|
||||
if (ry >= g->y + g->h) break;
|
||||
if (ry + g->cell_h <= g->y) continue;
|
||||
|
||||
if (g->draw_cb) {
|
||||
int st = (index == g->sel) ? MEOW_LAZYGRID_ST_SELECTED : 0;
|
||||
g->draw_cb(index, cx, ry, g->cell_w, g->cell_h, st, g->draw_ctx);
|
||||
} else {
|
||||
meow_lazygrid_item *it = lg_slot(g, index);
|
||||
if (!it) continue;
|
||||
lg_draw_cell(g, th, index, cx, ry, it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
meow_gfx_clip_pop();
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* meow_net_ipc.c — MeowOS SDK 网络自愿通道 → netmgrd IPC 桥(期4;追加式协议 0x00EE/0x00EF)。
|
||||
*
|
||||
* 与 meow_notify_ipc.c 同型:只做「一次性 connect + send + read」,不维护连接/订阅循环。
|
||||
* - meow_net_policy_check(host):MSG_NET_SDK op=NET_SDK_CHECK(app_id=MEOW_LOG_APP + host);
|
||||
* 读到 allow=1 → 返回 0(allow),allow=0 → 返回 1(block);任何失败(无 socket/超时/非法应答)
|
||||
* → 返回 0(乐观放行,绝不因监控不可用误封业务——NETMGR_DESIGN §6.3 自愿性质)。
|
||||
* - meow_net_report(rx,tx):MSG_NET_SDK op=NET_SDK_REPORT;send 后读一次应答头即关(不读文本),
|
||||
* best-effort,失败静默(返回值 void)。
|
||||
* 映射层(SDK 入参 ↔ IPC m_net_sdk_t 字段)集中在本文件;app_id 取 env MEOW_LOG_APP(与 meow_ui_log.c
|
||||
* 口径一致),socket 路径取 env MEOW_NET_SOCK(宿主/qemu 覆盖)缺省 /usr/data/meow_net.sock。
|
||||
*
|
||||
* 线程安全:app_id pthread_once 缓存;本文件纯传输层不调 meow_log(与 meow_notify_ipc.c 同约定)。
|
||||
*/
|
||||
#include "meow_net_ipc.h"
|
||||
#include "meow_ipc.h" /* 共享协议:帧头/m_net_sdk_t/m_net_sdk_result_t/MSG_NET_SDK* —— 防漂移 */
|
||||
|
||||
#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>
|
||||
|
||||
#define MEOW_NET_IPC_TIMEOUT_MS 500 /* 应答读超时:netmgrd 单线程 poll,>500ms 视为降级 */
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
const char *meow_net_ipc_app_id(void)
|
||||
{
|
||||
pthread_once(&g_once, ipc_init);
|
||||
return g_app_id;
|
||||
}
|
||||
|
||||
/* socket 路径:优先 env MEOW_NET_SOCK,缺省 /usr/data/meow_net.sock。 */
|
||||
static const char *ipc_sock_path(void)
|
||||
{
|
||||
const char *s = getenv("MEOW_NET_SOCK");
|
||||
return (s && s[0]) ? s : "/usr/data/meow_net.sock";
|
||||
}
|
||||
|
||||
/* 连接 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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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, rl = len;
|
||||
uint16_t ver = MEOW_IPC_VERSION, t = type;
|
||||
memcpy(hdr + 0, &magic, 4); memcpy(hdr + 4, &ver, 2); memcpy(hdr + 6, &t, 2);
|
||||
memcpy(hdr + 8, &req_id, 4); memcpy(hdr + 12, &rl, 4);
|
||||
if (!write_all(fd, hdr, sizeof hdr)) return 0;
|
||||
if (len && payload && !write_all(fd, payload, len)) return 0;
|
||||
return 1;
|
||||
}
|
||||
static void set_timeout(int fd)
|
||||
{
|
||||
struct timeval tv;
|
||||
tv.tv_sec = MEOW_NET_IPC_TIMEOUT_MS / 1000;
|
||||
tv.tv_usec = (MEOW_NET_IPC_TIMEOUT_MS % 1000) * 1000;
|
||||
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
|
||||
}
|
||||
|
||||
int meow_net_policy_check(const char *host)
|
||||
{
|
||||
if (!host || !host[0]) return 0;
|
||||
m_net_sdk_t q;
|
||||
memset(&q, 0, sizeof q);
|
||||
q.op = NET_SDK_CHECK;
|
||||
snprintf(q.app_id, sizeof q.app_id, "%s", meow_net_ipc_app_id());
|
||||
snprintf(q.host, sizeof q.host, "%s", host);
|
||||
|
||||
int fd = conn_path(ipc_sock_path());
|
||||
if (fd < 0) return 0; /* java socket:乐观放行 */
|
||||
set_timeout(fd);
|
||||
int allow = 0; /* 默认放行 */
|
||||
if (send_frame(fd, MSG_NET_SDK, 0x4E455443u /* "NETC" */, &q, (uint32_t)sizeof q)) {
|
||||
uint8_t rhdr[MEOW_IPC_HDR_LEN];
|
||||
if (read_all(fd, rhdr, sizeof rhdr)) {
|
||||
uint16_t rtype; uint32_t rlen;
|
||||
memcpy(&rtype, rhdr + 6, 2); memcpy(&rlen, rhdr + 12, 4);
|
||||
if (rtype == MSG_NET_SDK_RESULT && rlen >= 10) {
|
||||
uint8_t pay[12];
|
||||
if (read_all(fd, pay, 12)) { /* code(4)+allow(4)+text_len(2)+pad(2) */
|
||||
m_net_sdk_result_t res;
|
||||
memset(&res, 0, sizeof res);
|
||||
memcpy(&res.code, pay, 4);
|
||||
memcpy(&res.allow, pay + 4, 4);
|
||||
memcpy(&res.text_len, pay + 8, 2);
|
||||
if (res.text_len < sizeof res.text)
|
||||
read_all(fd, (uint8_t *)res.text, res.text_len);
|
||||
if (res.code == MEOW_OK) allow = (res.allow != 0) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
return allow ? 0 : 1; /* allow=1 → 返回 0(放行);allow=0 → 返回 1(block) */
|
||||
}
|
||||
|
||||
void meow_net_report(uint32_t rx, uint32_t tx)
|
||||
{
|
||||
m_net_sdk_t q;
|
||||
memset(&q, 0, sizeof q);
|
||||
q.op = NET_SDK_REPORT;
|
||||
snprintf(q.app_id, sizeof q.app_id, "%s", meow_net_ipc_app_id());
|
||||
q.rx = rx; q.tx = tx;
|
||||
int fd = conn_path(ipc_sock_path());
|
||||
if (fd < 0) return; /* 无 socket:静默 */
|
||||
set_timeout(fd);
|
||||
if (send_frame(fd, MSG_NET_SDK, 0x4E455452u /* "NETR" */, &q, (uint32_t)sizeof q)) {
|
||||
uint8_t rhdr[MEOW_IPC_HDR_LEN];
|
||||
read_all(fd, rhdr, sizeof rhdr); /* best-effort:读一次应答头即关,不解析 */
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* meow_net_ipc.h — MeowOS SDK 网络自愿通道 → netmgrd IPC 桥(内部头,不随 meow_ui.h 发布)。
|
||||
*
|
||||
* 定位(NETMGR_DESIGN §6.3/§4):让框架 App 的 meow_net_policy_check()/meow_net_report() 真正经
|
||||
* AF_UNIX IPC 到 netmgrd(/usr/data/meow_net.sock)。协议帧与负载结构复用共享 meow_ipc.h:
|
||||
* - MSG_NET_SDK(0x00EE) ↑:op=NET_SDK_CHECK(app_id+host)/ NET_SDK_REPORT(app_id+rx+tx);
|
||||
* - MSG_NET_SDK_RESULT(0x00EF) ↓:code + (CHECK)allow + 状态文本。
|
||||
* 约定:只做「一次性 connect + send + read」,不维护订阅循环(同 meow_notify_ipc.c 型);
|
||||
* 全部失败路径优雅降级(无 socket/写失败/超时/非法应答 → check 返回 0 放行、report 静默)。
|
||||
*/
|
||||
#ifndef MEOW_NET_IPC_H
|
||||
#define MEOW_NET_IPC_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "meow_ui.h" /* meow_net_policy_check / meow_net_report */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* 当前 app 的 app_id(env MEOW_LOG_APP,缺省 "app";pthread_once 缓存,线程安全)。 */
|
||||
const char *meow_net_ipc_app_id(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* MEOW_NET_IPC_H */
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* meow_select.c — §5.8.6 枚举/预设选择器(M7.2 设置页缺口 #4)。
|
||||
*
|
||||
* 封装 = BottomSheet(标题 + 选项列表)+ meow_listview(SINGLE) 单选列表。选项用 meow_listview
|
||||
* 渲染(单选高亮 primaryContainer、可滚动 meow_scroll 惯性);外层模态复用 meow_ui_modal_*
|
||||
* + meow_ui_modal_capture/restore(与 meow_dialog 同源:scrim 只叠一层/关闭无残影)。
|
||||
* 阻塞式(meow_input_pop 轮询);打开前排空残留触摸;点面板外/左缘返回 = 取消;点选项 = 选中 +
|
||||
* 关闭 + on_change 回调。选项数组定长(≤MEOW_SELECT_MAX_ITEMS)。
|
||||
*/
|
||||
#include "meow_ui.h"
|
||||
#include "include/meow_select.h"
|
||||
#include "meow_ui_internal.h" /* meow_input_pop/drain + meow_ui_modal_capture/restore */
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define MEOW_SELECT_ROW 48 /* 选项行高(px) */
|
||||
|
||||
struct meow_select {
|
||||
char title[96];
|
||||
char items[MEOW_SELECT_MAX_ITEMS][64];
|
||||
int nitems;
|
||||
int cur; /* 初始/当前选中项 */
|
||||
int result; /* 选中下标 / -1=取消 */
|
||||
int done;
|
||||
int h; /* 面板高度(0=自动) */
|
||||
meow_select_fn cb;
|
||||
void *ctx;
|
||||
};
|
||||
|
||||
/* meow_listview src2 数据源:填 title=options[idx](右尾件无;enabled=1)。 */
|
||||
static int select_src2(int idx, meow_list_item *out, void *ctx)
|
||||
{
|
||||
meow_select *s = (meow_select *)ctx;
|
||||
if (idx < 0 || idx >= s->nitems) return 0;
|
||||
memset(out, 0, sizeof *out);
|
||||
out->title = s->items[idx];
|
||||
out->right = MEOW_LIST_RIGHT_NONE;
|
||||
out->enabled = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
meow_select *meow_select_create(const char *title, int cur)
|
||||
{
|
||||
meow_select *s = (meow_select *)calloc(1, sizeof *s);
|
||||
if (!s) return NULL;
|
||||
if (title) { strncpy(s->title, title, sizeof s->title - 1); s->title[sizeof s->title - 1] = '\0'; }
|
||||
s->cur = cur; s->result = -1; s->done = 0;
|
||||
return s;
|
||||
}
|
||||
int meow_select_add_item(meow_select *s, const char *label)
|
||||
{
|
||||
if (!s || s->nitems >= MEOW_SELECT_MAX_ITEMS) return -1;
|
||||
if (label) { strncpy(s->items[s->nitems], label, 63); s->items[s->nitems][63] = '\0'; }
|
||||
int idx = s->nitems;
|
||||
s->nitems++;
|
||||
return idx;
|
||||
}
|
||||
void meow_select_set_on_change(meow_select *s, meow_select_fn cb, void *ctx)
|
||||
{ if (s) { s->cb = cb; s->ctx = ctx; } }
|
||||
|
||||
int meow_select_open(meow_select *s)
|
||||
{
|
||||
if (!s || s->nitems <= 0) return -1;
|
||||
const meow_theme *th = meow_theme_get();
|
||||
meow_input_start();
|
||||
meow_input_drain(); /* 排空残留触摸(goodix 长尾) */
|
||||
|
||||
s->done = 0; s->result = -1;
|
||||
int W = meow_ui_width(), H = meow_ui_height();
|
||||
int sb = meow_ui_statusbar_height(); if (sb < 0) sb = 0;
|
||||
int pad = 24;
|
||||
int title_h = meow_ui_text_height(th->font_title);
|
||||
int rowh = MEOW_SELECT_ROW;
|
||||
int avail = H - sb - 20;
|
||||
int min_h = pad + title_h + 12 + rowh + 20;
|
||||
int want = (s->h > 0) ? s->h : min_h;
|
||||
int est = pad + title_h + 12 + s->nitems * rowh + 20;
|
||||
if (est > want) want = est;
|
||||
if (want > avail) want = avail;
|
||||
if (want < min_h) want = (min_h < avail) ? min_h : avail;
|
||||
int ph = want, py = H - ph; /* 底部面板(顶部圆角) */
|
||||
int list_y = py + pad + title_h + 12;
|
||||
int list_h = ph - (list_y - py) - pad; /* 底部余量 */
|
||||
if (list_h < rowh) list_h = rowh;
|
||||
|
||||
/* 选项列表(meow_listview[SINGLE]:单选高亮 + 可滚动)。open 时创建,close 释放(冷路径)。 */
|
||||
meow_widget *lv = meow_listview_create(0, list_y, W, list_h, s->nitems,
|
||||
select_src2, s, MEOW_LIST_SEL_SINGLE);
|
||||
if (!lv) return -1;
|
||||
meow_listview_set_item_h(lv, rowh);
|
||||
/* 定位初始选中项在视口内。 */
|
||||
int target = s->cur * rowh - (list_h - rowh) / 2;
|
||||
int maxs = meow_listview_max_scroll(lv);
|
||||
if (target < 0) target = 0;
|
||||
if (target > maxs) target = maxs;
|
||||
meow_listview_set_scroll(lv, target);
|
||||
meow_listview_set_sel(lv, s->cur);
|
||||
meow_scroll *sc = meow_scroll_wrap(lv, 1); /* 惯性滚动(fling/snap) */
|
||||
|
||||
/* §6.4 模态作用域 + 背景快照(关闭即还原,无残影/不叠黑)。 */
|
||||
meow_ui_modal *m = meow_ui_modal_begin();
|
||||
meow_ui_modal_capture();
|
||||
/* 首次绘制:遮罩 + 底部面板(顶部圆角)+ 标题 + 选项列表。 */
|
||||
meow_ui_fill_rect_alpha(0, 0, W, H, 0x000000, 150);
|
||||
meow_gfx_fill_round(0, py, W, ph, th->radius_card, th->surfaceContainerHighest);
|
||||
meow_ui_text(pad, py + pad, s->title, th->font_title, th->onSurface);
|
||||
meow_widget_draw(lv);
|
||||
meow_ui_flush();
|
||||
|
||||
int dy_start = 0, down = 0;
|
||||
while (!s->done) {
|
||||
meow_input_ev e;
|
||||
while (meow_input_pop(&e)) {
|
||||
meow_ui_activity_ping(); /* 模态期间交互也重置息屏计时 */
|
||||
if (e.type == MEOW_EV_TOUCH_DOWN) { down = 1; dy_start = e.b; }
|
||||
else if (e.type == MEOW_EV_TOUCH_MOVE) {
|
||||
if (down && sc) {
|
||||
meow_scroll_track(sc, e.b - dy_start, meow_ui_time_ms());
|
||||
dy_start = e.b;
|
||||
}
|
||||
} else if (e.type == MEOW_EV_TAP) {
|
||||
int x = e.a, y = e.b;
|
||||
if (y < py) { s->done = 1; s->result = -1; break; } /* 点面板外 = 取消 */
|
||||
int idx = meow_listview_tap_at(lv, x, y); /* 单选高亮 = 选中 */
|
||||
if (idx >= 0) {
|
||||
s->result = idx; s->cur = idx;
|
||||
if (s->cb) s->cb(idx, s->ctx); /* 关闭即回调 */
|
||||
s->done = 1;
|
||||
}
|
||||
/* 面板内但未命中项:保持打开(等下一次点击)。 */
|
||||
} else if (e.type == MEOW_EV_SWIPE) {
|
||||
if (e.a == MEOW_SWIPE_LEFT) { s->done = 1; s->result = -1; } /* 返回 = 取消 */
|
||||
else if (sc) meow_scroll_fling(sc);
|
||||
}
|
||||
}
|
||||
meow_anim_tick(); /* 推进 fling */
|
||||
if (!s->done) usleep(16000);
|
||||
}
|
||||
meow_ui_modal_end(m);
|
||||
meow_ui_modal_restore();
|
||||
if (sc) meow_scroll_free(sc);
|
||||
meow_widget_free(lv);
|
||||
return s->result;
|
||||
}
|
||||
void meow_select_free(meow_select *s) { if (s) free(s); }
|
||||
|
||||
int meow_select_show(const char *title, const char *const *opts, int n, int cur)
|
||||
{
|
||||
if (!opts || n <= 0) return -1;
|
||||
meow_select *s = meow_select_create(title, cur);
|
||||
if (!s) return -1;
|
||||
for (int i = 0; i < n && i < MEOW_SELECT_MAX_ITEMS; i++) meow_select_add_item(s, opts[i]);
|
||||
int r = meow_select_open(s);
|
||||
meow_select_free(s);
|
||||
return r;
|
||||
}
|
||||
+7
-1
@@ -32,11 +32,13 @@
|
||||
* include/meow_ui_log.h 应用日志(§9)
|
||||
* include/meow_ui_notify.h 应用通知(§9.1)
|
||||
* include/meow_ui_config.h 应用配置读写(§9.2)
|
||||
* include/meow_ui_font.h 字体列表(§5.7)+ 字体分层(§10)
|
||||
* include/meow_ui_font.h 字体列表(§5.7)+ 字体分层(§10)+ 矢量字 meow_ft_*(§10.1)
|
||||
* include/meow_ui_audio.h 独占音频客户端(§11)
|
||||
* include/meow_ui_box.h 线性布局引擎(§12.1:meow_box,固定/weight/wrap/对齐/嵌套)
|
||||
* include/meow_ui_anim.h 动画基座+UI 节拍器(§12.2:tween/easing/池+脏区联动)
|
||||
* include/meow_ui_scroll.h 滚动物理(§12.3:速度跟踪/fling/边界回弹/snap)
|
||||
* include/meow_lazygrid.h 2 维虚拟化宫格(§12.4:meow_lazygrid,M7.4 主界面前置)
|
||||
* include/meow_select.h 枚举/预设选择器(M7.2 设置页缺口 #4:BottomSheet+单选列表)
|
||||
* include/meow_ui_str.h 字符串资源/i18n(§13:meow_str/meow_strf/meow_str_set_locale)
|
||||
*
|
||||
* 其余模块头(非伞头、App 按需显式 include,均保持不动):
|
||||
@@ -70,6 +72,10 @@
|
||||
#include "include/meow_ui_box.h"
|
||||
#include "include/meow_ui_anim.h"
|
||||
#include "include/meow_ui_scroll.h"
|
||||
#include "include/meow_lazygrid.h"
|
||||
#include "include/meow_select.h"
|
||||
#include "include/meow_ui_str.h"
|
||||
#include "include/meow_ui_net.h" /* 期4 网络自愿通道:meow_net_policy_check / meow_net_report */
|
||||
#include "include/meow_cachegov.h" /* 磁盘缓存 page-cache 治理(A:DONTNEED L0/L1 + refault 回退;C 归 svc_perf) */
|
||||
|
||||
#endif /* MEOW_UI_H */
|
||||
@@ -27,7 +27,7 @@
|
||||
static unsigned char *g_modal_cap = NULL;
|
||||
static int g_modal_cap_line = 0, g_modal_cap_h = 0, g_modal_cap_bpp = 0;
|
||||
|
||||
static void modal_capture(void)
|
||||
void meow_ui_modal_capture(void)
|
||||
{
|
||||
unsigned char *bb = meow_ui_back_buf();
|
||||
int line = meow_ui_back_buf_pitch();
|
||||
@@ -43,7 +43,7 @@ static void modal_capture(void)
|
||||
if (g_modal_cap) memcpy(g_modal_cap, bb, n);
|
||||
}
|
||||
|
||||
static void modal_restore(void)
|
||||
void meow_ui_modal_restore(void)
|
||||
{
|
||||
unsigned char *bb = meow_ui_back_buf();
|
||||
if (!g_modal_cap || !bb) return;
|
||||
@@ -235,7 +235,7 @@ void meow_dialog_show(meow_dialog *d)
|
||||
/* §6.4:模态作用域——本层打开期间下层(view/widget)完全收不到触摸;点卡片外由背板消费
|
||||
* (d->done=1 result=0 取消),不透传。嵌套弹层 depth 递增。 */
|
||||
meow_ui_modal *m = meow_ui_modal_begin();
|
||||
modal_capture(); /* M2.71-bug3:拍下背景,关闭即还原(scrim 只叠一层/无残影);
|
||||
meow_ui_modal_capture(); /* M2.71-bug3:拍下背景,关闭即还原(scrim 只叠一层/无残影);
|
||||
* 嵌套模态在各自打开时拍「当前已渲染帧」,故两层弹开不叠黑。 */
|
||||
dlg_paint(d, th);
|
||||
meow_ui_flush();
|
||||
@@ -291,7 +291,7 @@ void meow_dialog_show(meow_dialog *d)
|
||||
usleep(16000);
|
||||
}
|
||||
meow_ui_modal_end(m); /* §6.4:关闭模态作用域(下层恢复接收触摸) */
|
||||
modal_restore(); /* M2.71-bug3:关闭还原背景,无残影 */
|
||||
meow_ui_modal_restore(); /* M2.71-bug3:关闭还原背景,无残影 */
|
||||
}
|
||||
|
||||
int meow_dialog_result(meow_dialog *d) { return d ? d->result : 0; }
|
||||
@@ -355,7 +355,7 @@ int meow_sheet_open(meow_sheet *s)
|
||||
int ph = sheet_h(s, th);
|
||||
int py = H - ph, pad = 24;
|
||||
meow_ui_modal *m = meow_ui_modal_begin(); /* §6.4:模态作用域(下层收不到触摸) */
|
||||
modal_capture(); /* M2.71-bug3:拍下背景,关闭即还原(scrim 只叠一层/无残影) */
|
||||
meow_ui_modal_capture(); /* M2.71-bug3:拍下背景,关闭即还原(scrim 只叠一层/无残影) */
|
||||
/* 首次绘制:遮罩 + 底部面板(顶部圆角) */
|
||||
meow_ui_fill_rect_alpha(0, 0, W, H, 0x000000, 150);
|
||||
meow_gfx_fill_round(0, py, W, ph, th->radius_card, th->surfaceContainerHighest);
|
||||
@@ -389,7 +389,7 @@ int meow_sheet_open(meow_sheet *s)
|
||||
usleep(16000);
|
||||
}
|
||||
meow_ui_modal_end(m); /* §6.4:关闭模态作用域 */
|
||||
modal_restore(); /* M2.71-bug3:关闭还原背景,无残影 */
|
||||
meow_ui_modal_restore(); /* M2.71-bug3:关闭还原背景,无残影 */
|
||||
return s->result;
|
||||
}
|
||||
void meow_sheet_free(meow_sheet *s) { if (s) free(s); }
|
||||
@@ -137,6 +137,13 @@ unsigned char *meow_ui_back_buf(void);
|
||||
int meow_ui_back_buf_pitch(void);
|
||||
int meow_ui_back_buf_bpp(void);
|
||||
|
||||
/* ---- 模态背景快照/恢复(meow_ui_dialog.c 提供;dialog/sheet/select 共用)----
|
||||
* open 前调用 capture 把整帧背景拍下(modal_capture),close 后调用 restore 回填 + 整帧上屏
|
||||
* (modal_restore)→ scrim 每次只叠一层、关闭即还原无残影(M2.71-bug3)。共享同一份快照缓冲,
|
||||
* 各模态组件(meow_select 等)无需重复实现。 */
|
||||
void meow_ui_modal_capture(void);
|
||||
void meow_ui_modal_restore(void);
|
||||
|
||||
/* 实心圆角矩形(控件按钮底色用;公共 §3 只有空心 draw_rect_round)。[D3] */
|
||||
void meow_gfx_fill_round(int x, int y, int w, int h, int r, uint32_t color);
|
||||
/* 圆角矩形半透明填充(M2.71-bug4):禁用变暗等状态层用,圆角外不叠色(防灰方块)。 */
|
||||
|
||||
Reference in new issue
Block a user