- meow_ui_sys_set_system_mode: launcher置1才渲染, App默认0→不画状态栏; app_overlay_statusbar在launch_app循环~1s叠加28px顶条(不SIGSTOP App) - 数据源唯一(launcher g_sys→meow_ui_sys_feed; wifi_level/batt/vol/bri/clock), 根治"launcher有亮度App没有" - 右侧=MD3图标+变量值(statusbar_right_str文本退役→图标簇, 复用sb_icons.h/MEOW_SB_ICON_*/meow_gfx_blit_icon); 颜色随meow_theme - ui.style换图标集(meow_ui_style/set_style读ui.style md3/miuix, MIUIx暂MD3占位) - hello最小改动(redraw非全屏只清inset=28之下, 不冲系统顶条); 开放点: App不SIGSTOP/inset28/MIUIx占位/set_statusbar系统模式门 - meow_ui.a=3da5af7b launcher=a2b1c096 hello=67075741 0warning; 宿主test_sb_statusbar ALL PASS + render PPM目检(左时钟右亮度/WiFi/蓝牙/音量/电量) - 遗留: 真机验证(无双画/数据一致/顶条叠加/inset/ui.style); 运行时fullscreen沉浸叠画待M3; MIUIx图标占位
286 lines
12 KiB
C
286 lines
12 KiB
C
/* hello.c — MeowOS SDK 版示例应用(链 meow_ui.a,B1)。
|
||
*
|
||
* 契约(MEOW_APP_SDK.md §1/§3/§6):应用只调 meow_ui API —— 屏幕(back_buf +
|
||
* flush 上屏)与输入(meow_ui_run 事件循环)由框架接管;不再 open/mmap/ioctl
|
||
* /dev/fb0、不再读 /dev/input/event*。本应用验证「清屏 + 画文本 + 画按钮 +
|
||
* 响应触摸」这一最小工作闭环,并集成了通知触发按钮(meow_notify_send,§9.1)
|
||
* 用于端到端测试通知链路(SDK → IPC → launcher 中央存储 → 10s TTL 落盘)。
|
||
*
|
||
* 交互:
|
||
* - 点按钮 → tap 计数 +1(按钮标签实时更新);
|
||
* - 通知触发行(6 个按钮):每个点击 meow_notify_send() 发一条不同参数的通知,
|
||
* 覆盖 普通INFO / 媒体(单例) / HIGH紧急度 / 进度(40%) / 常驻 / 跳转Activity;
|
||
* - CANCEL LAST 按钮:meow_notify_cancel(最近一次 send 的 id);
|
||
* - 左缘左滑(抬手触发)→ 请求退出(回应用列表);滑动过程顶部出现随位移增长的进度条;
|
||
* - 空闲时 on_tick 每秒刷新一行状态(含运行秒数)。
|
||
*
|
||
* 宿主/qemu 离屏:设 MEOW_UI_DUMP=<ppm 路径> 时,首帧 flush 即把 back_buf 写成
|
||
* PPM 截图供检查;设备上无副作用。
|
||
*
|
||
* Build(见 build_hello.sh):
|
||
* zig-mipsel-cc -Os -static hello.c meow_ui.a libz.a -lpthread -o hello
|
||
*/
|
||
#include "meow_ui.h"
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
#include <stdlib.h>
|
||
#include <signal.h>
|
||
#include <unistd.h>
|
||
#include <time.h>
|
||
|
||
/* §6.1 ①:Activity 归属。主界面(根,stack 空时 active()=0)绑定全部按钮控件;
|
||
* 全屏沉浸页为独立 Activity(无控件)——触摸它绝不命中主界面按钮(根治跨页残留)。
|
||
* 进全屏 meow_ui_activity_push(ACT_FULL),退出 pop 回根(主界面按钮恢复可点,对象复用)。 */
|
||
#define ACT_HOME 0
|
||
#define ACT_FULL 1
|
||
|
||
static meow_widget *g_btn;
|
||
static meow_widget *g_nbtn[6]; /* 通知触发按钮行 */
|
||
static meow_widget *g_cancel; /* 取消最近一条通知 */
|
||
static meow_widget *g_fs_btn; /* FULLSCREEN 进入按钮(主界面) */
|
||
static int g_fullscreen = 0; /* 1=处于全屏视图(点任意处退出回主界面) */
|
||
static const char *g_notif_labels[6] = { "INFO", "MEDIA", "HIGH", "PROG", "ONGO", "TGT" };
|
||
static uint32_t g_last_notif = 0; /* 最近一次 send 返回的 id,供 cancel */
|
||
static int g_taps = 0;
|
||
static int g_secs = 0;
|
||
static char g_btn_label[32] = "TAP ME";
|
||
|
||
/* M2.51:统一日志改用 SDK meow_log()(按 app 分离 + 级别过滤 + 降级),
|
||
* 删除自造轮子的 hellolog()(自读 MEOW_LOG_DIR + snprintf + fopen/fprintf)。 */
|
||
static void on_sigterm(int s) { (void)s; meow_ui_request_exit(); }
|
||
|
||
/* 通知触发:对应第 which 个按钮(0..5)。SDK 端 meow_notify_send() 自动注册未声明
|
||
* 类型(本应用用到 4 种:GENERAL / MEDIA / PROGRESS / MESSAGE,均在 4 种上限内),
|
||
* 超时语义见 meow_ui.h §9.1;媒体类型为单例(重发替换)。 */
|
||
static void notif_send(int which)
|
||
{
|
||
meow_notify n;
|
||
memset(&n, 0, sizeof n);
|
||
n.progress = -1; /* 缺省不显示进度条 */
|
||
n.timeout_ms = MEOW_NOTIFY_TIMEOUT_NEVER;
|
||
n.urgency = MEOW_NOTIFY_URG_NORMAL;
|
||
const char *label = "?";
|
||
|
||
switch (which) {
|
||
case 0: /* 普通 INFO */
|
||
label = "INFO";
|
||
n.cat = MEOW_NOTIFY_CAT_GENERAL;
|
||
n.title = "测试-普通通知";
|
||
n.body = "普通 INFO 通知,timeout=5000ms";
|
||
n.timeout_ms = 5000;
|
||
break;
|
||
case 1: /* 媒体单例(ongoing,不自动过期) */
|
||
label = "MEDIA";
|
||
n.cat = MEOW_NOTIFY_CAT_MEDIA;
|
||
n.urgency = MEOW_NOTIFY_URG_LOW;
|
||
n.title = "测试-媒体通知";
|
||
n.body = "媒体单例,正在播放,timeout=-1 常驻";
|
||
n.timeout_ms = MEOW_NOTIFY_TIMEOUT_ONGOING;
|
||
break;
|
||
case 2: /* HIGH 紧急度 */
|
||
label = "HIGH";
|
||
n.cat = MEOW_NOTIFY_CAT_GENERAL;
|
||
n.urgency = MEOW_NOTIFY_URG_HIGH;
|
||
n.title = "测试-紧急度HIGH";
|
||
n.body = "高紧急度:顶部横幅+提示音";
|
||
n.timeout_ms = 3000;
|
||
break;
|
||
case 3: /* 进度通知 */
|
||
label = "PROG";
|
||
n.cat = MEOW_NOTIFY_CAT_PROGRESS;
|
||
n.title = "测试-进度通知";
|
||
n.body = "下载进度 40%";
|
||
n.timeout_ms = 8000;
|
||
n.progress = 40;
|
||
break;
|
||
case 4: /* 常驻(timeout=-1,不自动过期,需手动取消) */
|
||
label = "ONGO";
|
||
n.cat = MEOW_NOTIFY_CAT_GENERAL;
|
||
n.title = "测试-常驻通知";
|
||
n.body = "常驻不自动过期(timeout=-1),需手动取消";
|
||
n.timeout_ms = MEOW_NOTIFY_TIMEOUT_ONGOING;
|
||
break;
|
||
case 5: /* 带 target_activity + icon(SDK 结构含 icon 字段;NULL/空=用 app 默认图标) */
|
||
label = "TGT";
|
||
n.cat = MEOW_NOTIFY_CAT_MESSAGE;
|
||
n.title = "测试-跳转Activity";
|
||
n.body = "点击跳转 target_activity=main";
|
||
n.timeout_ms = 6000;
|
||
n.target_activity = "main";
|
||
n.icon = NULL; /* 空 → launcher 用 app 默认图标(app.json icon) */
|
||
break;
|
||
default:
|
||
return;
|
||
}
|
||
|
||
uint32_t id = meow_notify_send(&n);
|
||
g_last_notif = id;
|
||
meow_log(MEOW_LOG_INFO,
|
||
"notify btn=%s id=%u cat=%s urg=%s tmo=%d prog=%d target=%s title=%s",
|
||
label, (unsigned)id, meow_notify_category_str(n.cat),
|
||
meow_notify_urgency_str(n.urgency), n.timeout_ms, n.progress,
|
||
n.target_activity ? n.target_activity : "-",
|
||
n.title ? n.title : "-");
|
||
}
|
||
|
||
static void notif_cancel(void)
|
||
{
|
||
int r = meow_notify_cancel(g_last_notif);
|
||
meow_log(MEOW_LOG_INFO, "notify cancel id=%u result=%d", (unsigned)g_last_notif, r);
|
||
g_last_notif = 0;
|
||
}
|
||
|
||
/* 进入/退出全屏视图(MeowOS 全屏机制,§3.1):
|
||
* - fullscreen(1) + collapsed(1):状态栏「上收出屏只留提示线」(不完全沉浸,inset=4px),
|
||
* 顶部提示线热区仍可下拉,左缘返回手势(app 已注册 on_swipe)仍生效——SDK 自动处理,非 app 拦截;
|
||
* - fullscreen(0) + collapsed(0):恢复整条状态栏主界面。
|
||
* SDK 基座默认瞬切(g_sbar_collapse_anim_ms=0);如需过渡动画可先 meow_ui_statusbar_animate(ms)。
|
||
* §6.1 ①:全屏 = 独立 Activity——进时压栈 ACT_FULL(主界面控件因 owner=ACT_HOME 不再参与命中,
|
||
* 根治「全屏页触摸到主页按钮区域闪现」);退时弹栈回根,主界面按钮恢复可点(对象复用零重建)。 */
|
||
static void set_fullscreen(int on)
|
||
{
|
||
g_fullscreen = on ? 1 : 0;
|
||
meow_ui_set_fullscreen(g_fullscreen);
|
||
meow_ui_statusbar_set_collapsed(g_fullscreen);
|
||
if (on) meow_ui_activity_push(ACT_FULL);
|
||
else meow_ui_activity_pop();
|
||
meow_log(MEOW_LOG_INFO, g_fullscreen ? "fullscreen ON (collapsed), activity->%d"
|
||
: "fullscreen OFF (restore), activity->%d",
|
||
meow_ui_activity_active());
|
||
}
|
||
|
||
static void redraw(void)
|
||
{
|
||
const meow_theme *th = meow_theme_get();
|
||
int sb = meow_ui_statusbar_height(); /* A-0 全局状态栏 inset:内容下移避免被盖 */
|
||
if (g_fullscreen) {
|
||
/* 全屏页:纯色占满 + 居中提示;点任意处 / 左缘左滑(SDK on_swipe)返回 */
|
||
meow_ui_clear(th->accent);
|
||
meow_ui_text_center(240, 330, "FULLSCREEN ACTIVE", 28, 0xFFFFFF);
|
||
meow_ui_text_center(240, 366, "tap anywhere = back to home", 20, 0xFFFFFF);
|
||
meow_ui_text_center(240, 394, "(left-edge swipe also returns)", 20, 0xFFFFFF);
|
||
meow_ui_flush();
|
||
return;
|
||
}
|
||
/* inset:只清「状态栏之下」内容区(top sb px 保留给系统状态栏——由 launcher 进程叠加绘制;
|
||
* App 进程不渲染状态栏、也不冲掉它)。非全屏时 App 内容只在 y>=sb。 */
|
||
if (sb > 0) meow_ui_fill_rect(0, sb, 480, 720 - sb, th->bg);
|
||
else meow_ui_clear(th->bg);
|
||
meow_ui_text_center(240, sb + 8, "MEOWOS HELLO", 28, th->fg);
|
||
meow_ui_text_center(240, sb + 38, "SDK: meow_ui.a", 20, th->dim);
|
||
meow_widget_draw(g_btn);
|
||
meow_ui_text_center(240, 372, "tap = send notification (see log)", 20, th->dim);
|
||
for (int i = 0; i < 6; i++) meow_widget_draw(g_nbtn[i]);
|
||
meow_widget_draw(g_cancel);
|
||
meow_widget_draw(g_fs_btn);
|
||
char line[64];
|
||
snprintf(line, sizeof line, "taps=%d uptime=%ds", g_taps, g_secs);
|
||
meow_ui_text_center(240, 516, line, 20, th->dim);
|
||
meow_ui_text(20, 660, "left-swipe = exit", 20, th->dim);
|
||
meow_ui_flush();
|
||
}
|
||
|
||
static void on_tap(int x, int y)
|
||
{
|
||
if (g_fullscreen) {
|
||
/* 全屏页:点任意处退出回主界面(左缘左滑由 SDK on_swipe 处理,见下) */
|
||
set_fullscreen(0);
|
||
redraw();
|
||
return;
|
||
}
|
||
for (int i = 0; i < 6; i++) {
|
||
if (meow_widget_hit(g_nbtn[i], x, y)) {
|
||
notif_send(i);
|
||
redraw();
|
||
return;
|
||
}
|
||
}
|
||
if (meow_widget_hit(g_cancel, x, y)) {
|
||
notif_cancel();
|
||
redraw();
|
||
return;
|
||
}
|
||
if (meow_widget_hit(g_fs_btn, x, y)) {
|
||
set_fullscreen(1);
|
||
redraw();
|
||
return;
|
||
}
|
||
if (meow_widget_hit(g_btn, x, y)) {
|
||
g_taps++;
|
||
snprintf(g_btn_label, sizeof g_btn_label, "TAPPED x%d", g_taps);
|
||
/* 重建按钮标签(release 旧控件,重新 create 保持几何) */
|
||
meow_widget_free(g_btn);
|
||
g_btn = meow_btn_create(120, 300, 240, 64, g_btn_label);
|
||
redraw();
|
||
}
|
||
}
|
||
|
||
static void on_key(int keycode, int is_down)
|
||
{
|
||
/* §3.4.1:物理键语义由系统统一管理,APP 禁映射(电源=息屏/亮屏、音量=调音量、播放=媒体)。
|
||
* 这里仅记录日志,不做任何 exit/confirm 行为。 */
|
||
(void)keycode; (void)is_down;
|
||
meow_log(MEOW_LOG_DEBUG, "key"); /* 不处理:SDK 内部消化系统键语义 */
|
||
}
|
||
|
||
static void on_swipe_progress(int x_start, int dx)
|
||
{
|
||
/* 左滑过程实时回调:SDK 已自动画顶部进度条;这里演示 app 侧接入(画同长度条,幂等——
|
||
* 长度=当前 X=x_start+dx,与 SDK 自动画一致,不会误清条)。
|
||
* 抬手达标时 SDK 清条 + 触发 on_swipe(LEFT)→退出。 */
|
||
meow_ui_swipe_hint(x_start + dx);
|
||
}
|
||
|
||
static void on_swipe(int dir, int dx, int dy)
|
||
{
|
||
(void)dx; (void)dy;
|
||
if (dir == MEOW_SWIPE_LEFT) {
|
||
/* §6.1 导航栈语义:非根(全屏)→ 左滑返回主页(pop),不退出;根 → 退出回应用列表。
|
||
* 保证 on_swipe(LEFT) 在沉浸页=退出全屏回主页,而非直接退出整个 app。 */
|
||
if (g_fullscreen) { set_fullscreen(0); redraw(); }
|
||
else { meow_log(MEOW_LOG_INFO, "exit:swipe"); meow_ui_request_exit(); }
|
||
}
|
||
}
|
||
|
||
static void on_tick(void)
|
||
{
|
||
/* 每秒刷新一次状态行(避免 16ms 高频重绘) */
|
||
static uint64_t last = 0;
|
||
uint64_t now = meow_ui_time_ms();
|
||
if (now - last >= 1000) {
|
||
last = now;
|
||
g_secs++;
|
||
redraw();
|
||
}
|
||
}
|
||
|
||
int main(void)
|
||
{
|
||
meow_log(MEOW_LOG_INFO, "start");
|
||
meow_ui_opts o = { 480, 720 };
|
||
if (meow_ui_init(&o) != 0) { meow_log(MEOW_LOG_ERROR, "init failed"); return 1; }
|
||
/* M2.46 不再强制 set_mode 深色——主题跟随系统 display.theme/theme_mode(manual/auto+时段),
|
||
* 与 launcher s_ui_theme() 判定对齐(meow_theme_get() 已读 settings.conf)。 */
|
||
meow_theme_add_listener(NULL); /* noop: 预留主题变化重绘 */
|
||
|
||
signal(SIGTERM, on_sigterm); /* 看门狗返回键 → 干净退出 */
|
||
|
||
g_btn = meow_btn_create(120, 300, 240, 64, g_btn_label);
|
||
for (int i = 0; i < 6; i++)
|
||
g_nbtn[i] = meow_btn_create(3 + i * 80, 400, 74, 56, g_notif_labels[i]);
|
||
g_cancel = meow_btn_create(36, 464, 200, 42, "CANCEL LAST");
|
||
g_fs_btn = meow_btn_create(244, 464, 200, 42, "FULLSCREEN");
|
||
redraw();
|
||
|
||
meow_ui_events ev = { on_key, on_tap, on_swipe, on_tick, on_swipe_progress };
|
||
meow_ui_run(&ev);
|
||
|
||
for (int i = 0; i < 6; i++) meow_widget_free(g_nbtn[i]);
|
||
meow_widget_free(g_cancel);
|
||
meow_widget_free(g_fs_btn);
|
||
meow_widget_free(g_btn);
|
||
meow_ui_shutdown();
|
||
meow_log(MEOW_LOG_INFO, "exit");
|
||
return 0;
|
||
}
|