Files
MeowOS-SDK/meow_ui.h
T

363 lines
21 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* meow_ui.h — MeowOS 应用框架 SDK 公共 API(B1)。
*
* 蓝本:MEOW_APP_SDK.md §3-§8。应用只调本头声明的 API;绝不 open/mmap/ioctl
* /dev/fb0、绝不 open /dev/input/event* —— 屏幕与输入由 meow_ui 接管。
*
* 约定:
* - 坐标 0,0 = 左上;所有颜色为 24-bit RGB(0xRRGGBB),内部转 RGB565。
* - 所有绘制先进离屏 back_buf,调用 meow_ui_flush()/flush_region() 才上屏。
* - 文本字号:28(标题/状态栏)、20(正文/按钮)。
*
* 与设计文档的偏差(实现期定案,见 MEOW_APP_SDK.md §12 更新):
* [D1] meow_ui_opts 由实现补充(宽/高覆写,0=自动读 /dev/fb0);文档只引用未定义。
* [D2] meow_list_create 的数据源回调签名由实现定案(文档留占位注释)。
* [D3] meow_img_blend 内部实现补充(见 meow_ui_internal.h),非公共 API 增量。
* [D4] 增加 meow_theme_defaults()/meow_theme_current() 便于脱机(无 settings 文件)使用。
*/
#ifndef MEOW_UI_H
#define MEOW_UI_H
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/* ============================================================ */
/* 物理键 keycode(内核 input.h 常用值;KEEP 与 launcher/settings 一致) */
/* ============================================================ */
#define MEOW_KEY_POWER 116
#define MEOW_KEY_VOLUMEDOWN 114
#define MEOW_KEY_VOLUMEUP 115
#define MEOW_KEY_PLAYPAUSE 164
#define MEOW_KEY_PREVIOUS 165 /* KEY_PREVIOUSSONG=165(上一曲;19 是另一码,勿混用) */
#define MEOW_KEY_NEXT 163 /* KEY_NEXTSONG */
/* 滑动方向(on_swipe 的 dir 参数) */
#define MEOW_SWIPE_LEFT 0
#define MEOW_SWIPE_RIGHT 1
#define MEOW_SWIPE_UP 2
#define MEOW_SWIPE_DOWN 3
/* ============================================================ */
/* §3 绘制层 */
/* ============================================================ */
/* init 选项。width/height 为 0 表示自动读取 /dev/fb0 几何;
* 无 fb(宿主/qemu 离屏)时用 480×720 缺省([D1])。 */
typedef struct meow_ui_opts {
int width;
int height;
} meow_ui_opts;
int meow_ui_init(const meow_ui_opts *opts); /* 开 fb、读几何、载字库、建 back_buf;0=成功 */
void meow_ui_shutdown(void);
int meow_ui_width(void); /* 480 */
int meow_ui_height(void); /* 720 */
void meow_ui_clear(uint32_t color); /* 整屏清(填背景色) */
void meow_ui_put_px(int x, int y, uint32_t color);
void meow_ui_fill_rect(int x, int y, int w, int h, uint32_t color);
void meow_ui_draw_rect(int x, int y, int w, int h, uint32_t color); /* 空心框 */
void meow_ui_draw_rect_round(int x, int y, int w, int h, int r, uint32_t color); /* 圆角框 */
/* 文本(size=20/28) */
void meow_ui_text(int x, int y, const char *utf8, int size, uint32_t color);
void meow_ui_text_right(int right_x, int y, const char *utf8, int size, uint32_t color);
void meow_ui_text_center(int cx, int y, const char *utf8, int size, uint32_t color);
int meow_ui_text_width(const char *utf8, int size);
int meow_ui_text_height(int size); /* = meow_font_line_height */
/* 上屏:vsync + 原子 blit 脏区(内建脏区追踪) */
void meow_ui_flush(void);
void meow_ui_flush_region(int x, int y, int w, int h); /* 只刷新指定区 */
/* 半透明(blend565:前景按 alpha 与背景混叠) */
void meow_ui_fill_rect_alpha(int x, int y, int w, int h, uint32_t color, int alpha); /* 0-255 */
/* ============================================================ */
/* §4 图片层 */
/* ============================================================ */
typedef struct meow_img meow_img; /* 句柄,由 meow_img_* 管理,不透明 */
meow_img *meow_img_load_file(const char *path); /* 内部读文件 + stb 解码 */
meow_img *meow_img_load_mem(const void *buf, int len);
void meow_img_free(meow_img *img);
int meow_img_w(meow_img *img);
int meow_img_h(meow_img *img);
void meow_ui_img(int x, int y, meow_img *img); /* 原始尺寸 */
void meow_ui_img_scaled(int x, int y, int w, int h, meow_img *img); /* 最近邻缩放 */
void meow_ui_img_alpha(int x, int y, int w, int h, meow_img *img, int alpha);
void meow_ui_img_cover(int x, int y, int size, meow_img *img); /* 居中裁方 → size×size */
/* ============================================================ */
/* §5 控件层 */
/* ============================================================ */
typedef struct meow_widget meow_widget;
meow_widget *meow_btn_create(int x, int y, int w, int h, const char *label);
meow_widget *meow_slider_create(int x, int y, int w, int min, int max, int val);
meow_widget *meow_switch_create(int x, int y, int on);
/* 列表数据源回调:idx 有效时填 label 并返回 1;越界返回 0([D2] 定案签名) */
typedef int (*meow_list_source)(int idx, char *label, int len, void *ctx);
meow_widget *meow_list_create(int x, int y, int w, int h, int count,
meow_list_source src, void *ctx);
void meow_widget_free(meow_widget *w);
void meow_widget_draw(meow_widget *w); /* 每次 flush 前调用 */
int meow_widget_hit(meow_widget *w, int x, int y); /* 点是否落在控件内 */
void meow_slider_set(meow_widget *s, int val);
int meow_slider_get(meow_widget *s);
void meow_switch_set(meow_widget *w, int on);
int meow_switch_get(meow_widget *s);
int meow_btn_pressed(meow_widget *w); /* 应用在 on_tap 命中后标记按下 */
/* ============================================================ */
/* §5.1 输入法(IME)—— 内置触屏软键盘(仅英文 QWERTY / 数字,ASCII) */
/* ============================================================ */
typedef enum { MEOW_IME_ASCII, MEOW_IME_NUM } meow_ime_mode;
typedef struct meow_ime_ctx {
meow_ime_mode mode;
char buf[256]; /* 输入缓冲(初始值可预填) */
int maxlen; /* 最大字符数(含 NUL 预算) */
void (*on_change)(const char *text, void *ctx); /* 每次字符变化回调一次 */
void *ctx;
} meow_ime_ctx;
/* 阻塞:弹出键盘直到确认/取消。返回 1=确认(buf 已回填),0=取消。 */
int meow_ui_text_input(meow_ime_ctx *ctx);
/* 非阻塞:返回后 app 在 on_tick 里轮询 meow_ime_active()/meow_ime_text() */
void meow_ui_text_input_async(meow_ime_ctx *ctx);
int meow_ime_active(void);
const char *meow_ime_text(void);
/* ============================================================ */
/* §6 输入层 / 生命周期(B1) */
/* ============================================================ */
typedef struct meow_ui_events {
void (*on_key)(int keycode, int is_down); /* M2.24:物理键为系统语义,SDK 不下发;回调保留(应用不映射键为确认/exit) */
void (*on_tap)(int x, int y); /* 触摸抬起落点 */
void (*on_swipe)(int dir, int dx, int dy); /* 左滑=返回(左缘起点+抬手触发) */
void (*on_tick)(void); /* 空闲周期回调(动画/时钟) */
void (*on_swipe_progress)(int x_start, int dx);/* 左缘返回手势过程实时回调;x_start=按下X, dx=当前X-按下X */
} meow_ui_events;
void meow_ui_run(const meow_ui_events *ev); /* 阻塞:事件循环,直到 meow_ui_request_exit */
void meow_ui_request_exit(void); /* 请求退出(相当于返回/退出键) */
/* 顶部左滑进度条(视觉反馈):画一条 5px 高、从 x=0 到 x 的横条(主题 accent 色)。
* x<=0 时清掉此前画的条(填回背景色)。不发 flush,由调用方 meow_ui_flush_region 上屏。 */
void meow_ui_swipe_hint(int x);
/* ============================================================ */
/* §3.1 全局状态栏 / 全屏(A-0,SDK 系统级组件) */
/* ============================================================ */
/* 状态栏是 SDK 内置全局组件:所有界面默认显示(时钟/电量/WiFi/蓝牙/音量,slim ~40px),
* 数据由 SDK 内置 meow_ui_sys 读取器 1s 轻刷(与 launcher 同源单点一致)。
* z 顺序最顶:meow_ui_flush() 上屏前自动把状态栏画进 back_buf 顶部行,覆盖非全屏内容;
* 非全屏 APP 需用 meow_ui_statusbar_height() 把内容下移(inset),否则被状态栏盖住。 */
/* 状态栏显隐(默认显示;APP 声明全屏则隐藏——沉浸)。 */
void meow_ui_set_statusbar(int visible); /* 1=显示(默认) 0=隐藏(全屏) */
int meow_ui_statusbar_visible(void);
/* 全屏声明:等价 app.json fullscreen:1(外观上隐藏状态栏 + 左缘返回手势/进度条——沉浸)。 */
void meow_ui_set_fullscreen(int on);
/* 状态栏高度(px,供 APP 布局上移内容,避免被盖——status bar inset);
* 全屏/隐藏时返回 0。 */
int meow_ui_statusbar_height(void);
/* ---- 状态栏「上收出屏只留提示线」(collapsed,草案/待定案——本批仅加声明)----
* 供全屏调用:把状态栏内容上移出屏顶、只留一条「提示线」高度(可下拉还原)。
* 与 meow_ui_set_fullscreen(1) 的关系:fullscreen 隐藏整条状态栏(沉浸,inset=0);
* collapsed 仍视状态栏为「激活」,左缘返回手势(SWIPE_LEFT)与顶部伸缩提示线
* (meow_ui_swipe_hint)依旧生效,只是其内容上收、仅留提示线——用于「不完全沉浸」,
* 保留一处可下拉拉出状态栏的入口。顶部下滑热区 meow_ui_statusbar_hotzone() 在全屏时缩小。
* 实现位置:meow_ui_gfx.c(基座让 on 生效:true→只画提示线/内容上移);逐帧过渡动画
* (上收/恢复插值)为待定案,后续代理可经 meow_ui_statusbar_animate(ms) 补齐。 */
void meow_ui_statusbar_set_collapsed(int on); /* on=1 上收(内容上移出屏、只留提示线);on=0 恢复 */
int meow_ui_statusbar_collapsed(void); /* 当前是否处于「上收」目标(1=是) */
void meow_ui_statusbar_animate(int ms); /* TBD:上收/恢复过渡时长(ms);占位,后续代理实现插值 */
/* 顶部下滑热区(下拉展开状态栏/通知)高度(px)。
* 非全屏=整条状态栏可视高度;全屏时缩小到 MEOW_SBAR_HOTZONE_FS(保留一处下拉入口)。
* 返回「可用于起始下拉」的热区高度;由输入层/后续代理据此判定下拉起点。 */
int meow_ui_statusbar_hotzone(void);
#define MEOW_SBAR_HOTZONE_FS 4 /* 全屏时缩小后的顶部热区高度(px,草案值) */
/* ============================================================ */
/* §7 动画/时间(B1 最小可用) */
/* ============================================================ */
uint64_t meow_ui_time_ms(void); /* 单调毫秒时间戳 */
/* ============================================================ */
/* §7.1 显示刷新率调度(A-1/A-2:从 launcher M2.16 迁入,归口 SDK,PERF_SCHEDULER 显示子域) */
/* ============================================================ */
/* 设目标档位 60/30/15(launcher 与 app 统一走 SDK 的 present 帧率门控一致)。
* 同时把当前有效档对齐到目标(与 launcher settings_load 语义一致)。 */
void meow_ui_set_refresh_rate(int r);
/* 1=恒 max 不降档(性能优先);0=允许 governor 在 [15,30,60] 自适应降/升档。 */
void meow_ui_set_refresh_always_max(int on);
/* 当前有效帧率(governor 可能已降档;诊断用)。 */
int meow_ui_effective_rate(void);
/* 已呈现帧计数(验证 present 频率用)。 */
unsigned long long meow_ui_frames_presented(void);
/* ============================================================ */
/* §8 主题/配置 */
/* ============================================================ */
typedef enum { MEOW_THEME_LIGHT, MEOW_THEME_DARK, MEOW_THEME_AUTO } meow_theme_mode;
typedef struct meow_theme {
uint32_t bg; /* 背景色 */
uint32_t fg; /* 前景/正文 */
uint32_t accent; /* 强调色(按钮/选中) */
uint32_t dim; /* 次要文字 */
int font_title; /* 28 */
int font_body; /* 20 */
} meow_theme;
const meow_theme *meow_theme_get(void);
void meow_theme_set_mode(meow_theme_mode m);
meow_theme_mode meow_theme_get_mode(void);
typedef void (*meow_theme_listener)(meow_theme_mode m);
void meow_theme_add_listener(meow_theme_listener cb);
void meow_theme_remove_listener(meow_theme_listener cb);
/* [D4] 脱机访问两套内置色板(无 settings 文件也可靠):返回当前解析生效的色板副本。 */
meow_theme meow_theme_defaults(meow_theme_mode m);
/* ============================================================ */
/* §9 应用日志(统一日志 API,M2.51 最小可用版) */
/* ============================================================ */
/* app 只需一行 meow_log();SDK 管「写哪/怎么写/过滤/降级/上限」:
* - 路径:<MEOW_LOG_DIR>/apps/<MEOW_LOG_APP>/logs/<启动时间>_<pid>.log
* (目录不存在 mkdir -p,按 app 分目录),每次启动=新文件,同一次启动内 O_APPEND 追加;
* - 级别过滤:lvl < meow_log_get_level() 丢弃;默认 MEOW_LOG_INFO;
* - 格式:<monotonic_ms> [<app_id>] <LEVEL> message\n;
* - 上限:单文件 ≥2MB 自动开新文件追加 _<n>;每 App 总量 ≥50MB 按期删最旧,防撑爆卡;
* - 无卡/写失败降级 stderr,不阻塞 app 不崩;线程安全。
* 注意:MEOW_LOG_APP 由 launcher 注入;未注入时默认 app_id="app"。 */
typedef enum { MEOW_LOG_DEBUG=0, MEOW_LOG_INFO, MEOW_LOG_WARN, MEOW_LOG_ERROR } meow_log_level;
void meow_log(meow_log_level lvl, const char *fmt, ...); /* app 一行调用,写到按 app 分离日志 */
void meow_log_set_level(meow_log_level lvl); /* 可选:app 设本 app 级别;默认 MEOW_LOG_INFO */
meow_log_level meow_log_get_level(void);
/* ============================================================ */
/* §9.1 应用通知(meow_notify_*,M2.53 最小可用版) */
/* ============================================================ */
/* app 只需声明类型 + meow_notify_send() 即可发通知,SDK 管配额/活动表/降级:
* - 类型注册:app 声明支持的类型(最多 MEOW_NOTIFY_MAX_TYPES 4 种);
* meow_notify_send 也会自动注册未声明类型(受同样 4 种上限约束);
* - 配额(NOTIFICATION_SYSTEM.md §7,2026-08-29 用户拍板):每 App 最多 4 种类型;
* 每类型活动(未过期)通知上限 4,满则丢最旧(替换);媒体类型单例(每 app 仅
* 保留最近 1 条,重发替换——可多媒体 App);
* - 超时语义 [实现定案,沿用 NOTIFICATION_SYSTEM.md §2]:timeout_ms>0 = 过期毫秒数;
* 0 = 不自动过期(直到 meow_notify_cancel);-1 = 常驻(ONGOING),同样不自动过期;
* - 落点:本阶段 SDK 内部维护活动通知表 + 用 meow_log() 打日志作为落点;真实投递到
* 通知中心 UI / D-Bus(org.freedesktop.Notifications)是后续任务——本结构已对齐
* freedesktop 字段(summary/body/…)为此预留,但不写死后端;
* - 线程安全;不依赖 /dev/fb0、不依赖板子,可宿主编译。 */
#define MEOW_NOTIFY_MAX_TYPES 4 /* 每 App 最多 4 种通知类型 */
#define MEOW_NOTIFY_MAX_PER_TYPE 4 /* 每类型活动(未过期)通知上限 */
#define MEOW_NOTIFY_MAX_MEDIA 1 /* 媒体通知每 App 仅 1 条(单例) */
#define MEOW_NOTIFY_TIMEOUT_ONGOING (-1) /* 常驻(ONGOING),不自动过期 */
#define MEOW_NOTIFY_TIMEOUT_NEVER 0 /* 不自动过期,直到取消 */
/* 通知分类(freedesktop.org Desktop Notifications 的 category 最小子集 [实现定案]) */
typedef enum {
MEOW_NOTIFY_CAT_GENERAL = 0, /* 通用(默认) */
MEOW_NOTIFY_CAT_MESSAGE = 1, /* 即时消息/聊天 */
MEOW_NOTIFY_CAT_EMAIL = 2, /* 邮件 */
MEOW_NOTIFY_CAT_EVENT = 3, /* 事件/日历提醒 */
MEOW_NOTIFY_CAT_PROGRESS = 4, /* 进度/下载 */
MEOW_NOTIFY_CAT_MEDIA = 5, /* 媒体播放(MPRIS;每 app 单例) */
MEOW_NOTIFY_CAT_COUNT = 6, /* 边界哨兵(非合法类型) */
} meow_notify_category;
/* 紧急度(UI 视觉分级 + 提示音/震动强弱) */
typedef enum {
MEOW_NOTIFY_URG_LOW = 0, /* 低:仅进通知中心,不打扰 */
MEOW_NOTIFY_URG_NORMAL = 1, /* 普通 */
MEOW_NOTIFY_URG_HIGH = 2, /* 高:顶部横幅 + 提示音 */
MEOW_NOTIFY_URG_CRITICAL = 3, /* 紧急:需立刻注意 */
MEOW_NOTIFY_URG_COUNT = 4, /* 边界哨兵(非合法值) */
} meow_notify_urgency;
/* 发送负载(对齐 freedesktop Notify() 字段;SDK 内部复制副本,不持 app 外部指针,
* app 只需保证 icon/title/body/target_activity 指针在调用返回前有效)。 */
typedef struct meow_notify {
meow_notify_category cat; /* 类型(未声明则按 4 种上限自动注册) */
meow_notify_urgency urgency; /* 紧急度(缺省 MEOW_NOTIFY_URG_NORMAL;越界钳位) */
const char *title; /* summary:标题,单行(必填,NULL 则发送失败) */
const char *body; /* body:正文,可多行(可 NULL) */
const char *icon; /* app_icon:图标 icon name 或 path;NULL/空 = 用该 app 默认图标(launcher 按 app_id 回退) */
int timeout_ms; /* 见上超时语义;推荐 MEOW_NOTIFY_TIMEOUT_* */
const char *target_activity; /* 通知中心点击跳转的 Activity 名(可 NULL;M3 导航预留) */
int progress; /* 进度 0-100;-1=不显示进度条(缺省 -1) */
} meow_notify;
/* 类型注册:app 声明支持的类型。0=成功;-1=已声明/已达 4 种上限/非法。 */
int meow_notify_register_type(meow_notify_category cat);
/* 发送一条通知。返回通知 id(用于 meow_notify_cancel);失败返回 0。
* 同类型满 4 条时丢弃最旧(替换);媒体类型单例(重发替换同 app 上一条媒体通知)。 */
uint32_t meow_notify_send(const meow_notify *n);
/* 取消一条通知。1=成功;0=未找到(id 无效/已过期/已取消)。 */
int meow_notify_cancel(uint32_t id);
/* 实时更新一条活动通知的进度(0-100,-1=清除进度条)。local_id=meow_notify_send 的返回值。
* 语义(用户定案):仅改 SDK 内部活动槽 + 经 IPC 通知 launcher 改**内存活跃槽**;不落盘——
* TF 快照只在「创建时 / 结束(100% 或 dismiss)」触发。返回 1=找到并更新;0=未找到/非法。 */
int meow_notify_update_progress(uint32_t local_id, int progress);
/* 查询当前活动(未过期、未取消)通知数/是否还有。 */
int meow_notify_active_count(void);
int meow_notify_count_in_type(meow_notify_category cat);
int meow_notify_has_active(void); /* 1=尚有活动通知,0=无 */
/* 紧急度/分类 → 字符串("low"/"normal"/"high"/"critical"、"general"/"message"/…)。 */
const char *meow_notify_urgency_str(meow_notify_urgency u);
const char *meow_notify_category_str(meow_notify_category cat);
/* ============================================================ */
/* §10 字体分层 / 大字库(M2.5x:基座声明 + font 域已实现按档装载) */
/* ============================================================ */
/* 字体「档位」概念:launcher 按 app.json 声明的字库档("fonts":<档>)向 app 注入
* MEOW_FONT_TIER=<0|1|2>;SDK(meow_ui_gfx / meow_font 大字库加载)据档选库:
* MEOW_FONT_TIER_COMMON=0 默认 4K 常用字库;
* MEOW_FONT_TIER_GBK =1 大字库 GBK;
* MEOW_FONT_TIER_FULL =2 Full(全字符集)。
* 大字库生成由 font/gen/gen_font.py --charset 按档产出;按档装载由 meow_font_reload_tier()
* 实现(先 unload 旧再 load 新,释放旧 atlas 防大字库双驻留)。launcher 经
* meow_font_effective_paths_tiered() 按档解析大字库绝对路径并注入 MEOW_FONT_PNG/IDX。
* 既有 meow_font_* 符号(class_of/has_glyph/load_slot/config_multifont…)保留不动。
* 判缺字:meow_font_has_glyph(cp) 预判当前字库是否含该码位。 */
#define MEOW_FONT_TIER_COMMON 0
#define MEOW_FONT_TIER_GBK 1
#define MEOW_FONT_TIER_FULL 2
/* 设当前字体档(0/1/2,越界 clamp 到合法值),返回生效档。
* meow_ui_gfx.c 定义并触发 meow_font_reload_tier() 真重载(大字库切换释放旧 atlas)。 */
int meow_font_set_tier(int tier);
/* 当前字体档(初始 = MEOW_FONT_TIER_COMMON;meow_ui_init 时读 MEOW_FONT_TIER env 对齐)。 */
int meow_font_tier(void);
#ifdef __cplusplus
}
#endif
#endif /* MEOW_UI_H */