feat: M6.10–M6.19 批量收编 + M6.19 推卡验收

- M7 launcher 重写 5 期(虚拟化宫格/meow_theme 单一权威/横幅动画/自定义 PIN 键盘)
- OOM 通用化(cachegov 上收 SDK)、自动切歌根治、meowpanel jv_free 泄漏修复、Hello 滚动卡顿修复
- ncm 歌词 UI / 媒体通知封面 / 封面分辨率 640
- 5 bug 修复:ncm 后台化 UI 叠层+点击穿透、再进 ncm 桌面叠层、播放页新旧 UI 叠层抢刷脏区(meow_ui_gfx 恢复边沿整帧重画)、PIN 亮屏亮度跳(keys.c meow_panel_on 按 settings_brightness_clamped 恢复)、通知中心封面显歌单封面(cover.c save_cover_raw 加 is_song + 命中块守卫)
- 四 App 统一重链新 meow_ui.a 51caee4a;M6.19=0.8.19 固件 2d2331f2 推卡自刷验收全绿
This commit is contained in:
AskaEth committed 2026-09-03 12:16:01 +08:00
1 parent c2ee7626a4
commit 3d70a3dd2c
176 files changed
+15507 -597

No files matched your search

+246 -5
View File
@@ -38,6 +38,16 @@ static const int lk_keys[LK_ROWS][LK_COLS] = {
{ LK_ACT_CLR, 0, LK_ACT_BS },
};
/* ---- M7.1b 锁屏控件化:PIN 数字键盘 / 时钟 / 提示 / PIN 圆点 改用 SDK 控件 ----
* 旧自绘锁屏 UI 保留在 #if 0(回退位,见 lk_draw/lk_key_hit),新 SDK 渲染默认启用;
* run_lock_screen 会话骨架(EVIOCGRAB / 自睡 / 解锁脏区修复 / 物理键转发)不动,只替换
* 它调用的绘制/命中函数体。控件 owner 钉 LK_SENTINEL_ACT(子页 0x52 之外的独立哨兵)。 */
#define LK_SENTINEL_ACT 0x51 /* 锁屏控件 owner 哨兵(与子页 SUB_SENTINEL_ACT=0x52 区分) */
#define LK_DOWN_NONE (-10) /* 无键按下哨兵(关键值范围 LK_ACT_CLR=-2/-1/0-9) */
static meow_widget *lk_key_w[LK_ROWS][LK_COLS]; /* 12 键按钮(meow_widget_custom) */
static int lk_widgets_ready = 0;
static int g_lk_down_key = LK_DOWN_NONE; /* 当前按下的键(跟手指按压态) */
/* 键盘按钮 row*3+col 的命中矩形(x,y,w,h) */
static void lk_key_rect(int row, int col, int *x, int *y, int *w, int *h)
{
@@ -79,6 +89,121 @@ static void lk_draw_pin(int entered, const meow_ui_theme_t *th)
lk_pin_dot(x0 + i * gap, y, r, i < entered, th->primary, th->surface);
}
/* ---- M7.1b 控件化:SDK meow_widget 键盘 + meow_font 文字 ---- */
/* 数字键标签(1-9/0 单字符;static 缓冲各调用立即消费,无跨调用残留) */
static const char *lk_key_digit(int key)
{
static char d[2];
d[0] = (char)('0' + key);
d[1] = 0;
return d;
}
/* 键渲染:surfaceContainer 圆角底(MD3)+ 按压态 primary 26% α + 居中标签(SDK meow_font)。
* 颜色全部取 meow_theme_get()(SDK 单一权威:MD3/MIUIx × 明暗自动);读 w->state 的
* MEOW_WS_PRESSED 位(跟手指,由 lk_set_down_key 置/清)。 */
static void lk_key_render(meow_widget *w, const meow_theme *th, void *ctx)
{
int key = (int)(intptr_t)ctx;
int rc[4]; meow_widget_rect(w, rc); /* 绝对坐标 x,y,w,h(控件不透明,用 SDK 只读几何) */
int x = rc[0], y = rc[1], ww = rc[2], hh = rc[3];
unsigned st = meow_widget_state(w);
int rad = (th->radius > 0) ? th->radius : 10;
meow_gfx_fill_round(x, y, ww, hh, rad, th->surfaceContainer);
if (st & MEOW_WS_PRESSED)
meow_gfx_fill_round_alpha(x, y, ww, hh, rad, th->primary, 66);
const char *label;
int size;
if (key >= 0) { size = 30; label = lk_key_digit(key); }
else if (key == LK_ACT_CLR) { size = 22; label = mfont_ready ? "清空" : "C"; }
else { size = 22; label = mfont_ready ? "退格" : "DEL"; }
int cx = x + ww / 2;
int cy = y + hh / 2 - meow_ui_text_height(size) / 2;
meow_ui_text_center(cx, cy, label, size, th->onSurface);
}
/* 懒创建 12 键(owner=LK_SENTINEL_ACT;仅首次调用建一次)。布局几何沿用 lk_key_rect。 */
static void lk_widgets_build(void)
{
if (lk_widgets_ready) return;
meow_ui_activity_push(LK_SENTINEL_ACT);
for (int r = 0; r < LK_ROWS; r++) {
for (int c = 0; c < LK_COLS; c++) {
int x, y, w, h; lk_key_rect(r, c, &x, &y, &w, &h);
lk_key_w[r][c] = meow_widget_custom_create(x, y, w, h, lk_key_render, NULL,
(void *)(intptr_t)lk_keys[r][c]);
}
}
meow_ui_activity_pop();
lk_widgets_ready = 1;
}
/* 键值 -> 按钮(按压态置/清位用)。 */
static meow_widget *lk_widget_for_key(int key)
{
for (int r = 0; r < LK_ROWS; r++)
for (int c = 0; c < LK_COLS; c++)
if (lk_keys[r][c] == key) return lk_key_w[r][c];
return NULL;
}
/* 坐标 -> 键值(SDK meow_widget_hit;owner==active 已由调用方保证)。未命中返回 LK_DOWN_NONE。 */
static int lk_key_value_at(int x, int y)
{
for (int r = 0; r < LK_ROWS; r++)
for (int c = 0; c < LK_COLS; c++)
if (lk_key_w[r][c] && meow_widget_hit(lk_key_w[r][c], x, y))
return lk_keys[r][c];
return LK_DOWN_NONE;
}
/* 更新按压态(跟手指):清旧键状态位、置新键;返回 1=按键变化(调用方据此重绘)。 */
static int lk_set_down_key(int key)
{
if (key == g_lk_down_key) return 0;
meow_widget *old = lk_widget_for_key(g_lk_down_key);
if (old) meow_widget_set_state(old, MEOW_WS_PRESSED, 0);
meow_widget *nw = lk_widget_for_key(key);
if (nw) meow_widget_set_state(nw, MEOW_WS_PRESSED, 1);
g_lk_down_key = key;
return 1;
}
/* PIN 输入圆点(● 遮罩:已输入=primary 实心;未输入=primary 环 + surface 孔)。 */
static void lk_draw_pin_dots(int entered, const meow_theme *mt)
{
int cx = fb_xres / 2;
int y = 210;
int gap = 48, r = 20;
int x0 = cx - (3 * gap) / 2;
for (int i = 0; i < LCK_PIN_LEN; i++) {
int dx = x0 + i * gap;
fill_circle(dx, y, r, mt->primary);
if (i >= entered) fill_circle(dx, y, r - 3, mt->surface);
}
}
/* 键命中:旧自绘(lk_key_rect+mt_in)保留在 #if 0(回退位);M7.1b 走 SDK meow_widget_hit
* (owner==active + enabled 双闸门在 SDK 内;命中点=按下原点 mt->down_x/down_y,与原 mt_in 同源)。 */
#if 0
static int lk_key_hit(const meow_touch *mt, int row, int col)
{
int x, y, w, h; lk_key_rect(row, col, &x, &y, &w, &h);
return mt_in(mt, x, y, w, h);
}
#else
static int lk_key_hit(const meow_touch *mt, int row, int col)
{
if (!lk_key_w[row][col]) return 0;
return meow_widget_hit(lk_key_w[row][col], mt->down_x, mt->down_y);
}
#endif
#if 0
/* ============ 旧自绘锁屏整帧(M7.1b 回退位)============
* 原 lk_draw:fill_rect 全屏底 + lk_text(时钟/状态)+ lk_draw_pin(●)+ fill_round_rect 键盘。
* 一键回退:把下面 #if 0 改成 #if 1(并把新 lk_draw 的 #else 分支由 #else 改走旧轮廓),设备即回旧观感。 */
static void lk_draw(int entered, const char *status, time_t locked_until)
{
const meow_ui_theme_t *th = s_ui_theme();
@@ -116,6 +241,36 @@ static void lk_draw(int entered, const char *status, time_t locked_until)
fb_pan_buf0_force();
meow_ui_flush();
}
#else
/* ============ M7.1b 锁屏控件化整帧(默认启用)============
* 整帧不透明 surface 底(§310 脏区修复关键:全屏覆盖,无 App 帧透出)+ SDK 字体时钟/提示
* + 主题色 PIN 圆点 + meow_widget 键盘(surfaceContainer 圆角 / 按压态 primary 26% α / MD3 主题)。
* 颜色全部取 meow_theme_get()(SDK 单一权威:MD3/MIUIx × 明暗自动)。 */
static void lk_draw(int entered, const char *status, time_t locked_until)
{
const meow_theme *mt = meow_theme_get();
meow_ui_fill_rect(0, 0, fb_xres, fb_yres, mt->surface); /* 全屏不透明表面底 */
lk_widgets_build(); /* 懒创建 12 键 */
/* 时钟/提示(居中,SDK meow_font):锁定倒计时标题 */
meow_ui_text_center(fb_xres / 2, 120, locked_until > 0 ? "已锁定" : "输入 PIN 解锁",
24, mt->onSurface);
/* PIN 输入圆点(● 遮罩) */
lk_draw_pin_dots(entered, mt);
/* 状态行:错误提示(PIN 错误)着 error;倒计时/其它信息用 onSurfaceVariant */
int is_err = status[0] && (strstr(status, "PIN 错误") != NULL ||
strstr(status, "WRONG PIN") != NULL);
uint32_t status_col = status[0] ? (is_err ? mt->error : mt->onSurfaceVariant)
: mt->onSurfaceVariant;
meow_ui_text_center(fb_xres / 2, 260, status, 18, status_col);
/* 12 键(SDK meow_widget;命中走 lk_key_hit → meow_widget_hit) */
for (int r = 0; r < LK_ROWS; r++)
for (int c = 0; c < LK_COLS; c++)
if (lk_key_w[r][c]) meow_widget_draw(lk_key_w[r][c]);
meow_dirty_full();
fb_pan_buf0_force();
meow_ui_flush();
}
#endif
/* 阻塞式锁屏页。返回 1=已解锁,0=锁屏期间电源键息屏。 */
int run_lock_screen(void)
@@ -159,7 +314,26 @@ int run_lock_screen(void)
return 1;
}
lk_draw(entered, status, lock_until); /* 首帧(fill_rect 全屏不透明主题底 + 键盘) */
/* M7.1 解锁恢复:锁屏是整帧不透明覆盖(lk_draw fill_rect 全屏),退出后底层界面不会自动重绘
* —— 只有 App 自绘的控件区域刷新,未刷新区残留锁屏 PIN 最后一帧(解锁后整屏脏区根因)。此处
* 在锁屏首扑前把底层帧(SDK back_buf = 进入前的 App 帧/主界面帧)快照下来;解锁(unlocked=1)
* 时整帧回写 back_buf + present,强制底层界面完整重绘(app_launcher.c 的 lock-overlay 关闭路径
* 随后 pm_capture_app_frame 读 fb0 即得到正确 App 帧,不再读到锁帧)。 */
unsigned char *lk_bg_snap = NULL;
size_t lk_bg_len = 0;
if (back_buf_ok && fb && fb_line > 0 && fb_yres > 0) {
lk_bg_len = (size_t)fb_line * (size_t)fb_yres;
lk_bg_snap = malloc(lk_bg_len);
if (lk_bg_snap) memcpy(lk_bg_snap, fb, lk_bg_len);
else { lk_bg_len = 0; logmsg("lock: bg snapshot malloc fail (len=%zu)", lk_bg_len); }
}
/* M7.1b:锁屏控件 owner 闸门打开(meow_widget_hit / 按压态需 active==LK_SENTINEL_ACT)。
* 会话骨架(EVIOCGRAB/自睡/解锁脏区修复)不受影响;done/died 处对称 pop。 */
meow_ui_activity_push(LK_SENTINEL_ACT);
g_lk_down_key = LK_DOWN_NONE;
lk_draw(entered, status, lock_until); /* 首帧(全屏不透明主题底 + 控件化键盘) */
int unlocked = 0;
unsigned long long entry_ms = now_ms();
@@ -173,6 +347,9 @@ int run_lock_screen(void)
unsigned long long last_act = now_ms();
int asleep = 0;
int prev_pwr_down = 0;
/* M7.1 锁屏期物理键保留语义(音量/媒体键):边沿跟踪基线(锁屏是 EVIOCGRAB 独占真模态,
* 这些键只能在本层显式处理,不会转发到系统)。 */
int prev_vold = 0, prev_volu = 0, prev_play = 0, prev_prv = 0, prev_nxt = 0;
for (;;) {
time_t now = time(NULL);
int redraw = 0;
@@ -246,6 +423,7 @@ int run_lock_screen(void)
meow_panel_on();
asleep = 0;
last_act = now_ms();
g_lk_down_key = LK_DOWN_NONE; /* M7.1b:唤醒复位按压态基线 */
lk_draw(entered, status, lock_until); /* 重画锁屏页回锁 */
continue;
}
@@ -256,6 +434,50 @@ int run_lock_screen(void)
meow_panel_off();
break; /* return 0 */
}
/* M7.1 锁屏期物理键仍执行其语义(用户定案:音量键调音量、媒体键控播放/上下曲)。
* 锁屏层 EVIOCGRAB 独占这些输入(真模态),不转发系统,故必须在此显式处理,不能依赖别处。
* 音量沿用 launcher 既有递增/递减路径(settings_volume_adj —— 起 1% 线性渐变,
* 长按由下方 vol_ramp_tick 每圈推进);播放/上下曲经 meow_media_playctl 下发媒体指令。 */
int vold = (kp_keys.ok) && kp_is_down(&kp_keys, KEY_VOLUMEDOWN);
int volu = (kp_keys.ok) && kp_is_down(&kp_keys, KEY_VOLUMEUP);
int play = (kp_keys.ok) && kp_is_down(&kp_keys, KEY_PLAYPAUSE);
int prv = (kp_keys.ok && kp_is_down(&kp_keys, KEY_PREVIOUSSONG)) ||
(kp_power.ok && kp_is_down(&kp_power, KEY_PREVIOUSSONG));
int nxt = (kp_keys.ok && kp_is_down(&kp_keys, KEY_NEXT)) ||
(kp_power.ok && kp_is_down(&kp_power, KEY_NEXT));
if (vold && !prev_vold) { settings_volume_adj(-settings_volume_step); last_act = now_ms(); }
if (volu && !prev_volu) { settings_volume_adj(+settings_volume_step); last_act = now_ms(); }
if (play && !prev_play) {
const meow_media_session_t *ms = meow_media_current();
if (ms) {
uint32_t c = (ms->state == MEOW_MEDIA_PLAYING) ? 2u : 1u; /* 暂停/播放 */
int rc = meow_media_playctl(c, 0);
logmsg("lock: play key -> media cmd=%u rc=%d (%s)", c, rc, ms->app_id);
} else {
logmsg("lock: play key -> media-reserved (no media source)");
}
last_act = now_ms();
}
if (prv && !prev_prv) {
if (meow_media_current()) { int rc = meow_media_playctl(3, 0); logmsg("lock: prev key -> cmd=3 rc=%d", rc); }
else logmsg("lock: prev key -> media-reserved (no media source)");
last_act = now_ms();
}
if (nxt && !prev_nxt) {
if (meow_media_current()) { int rc = meow_media_playctl(4, 0); logmsg("lock: next key -> cmd=4 rc=%d", rc); }
else logmsg("lock: next key -> media-reserved (no media source)");
last_act = now_ms();
}
prev_vold = vold; prev_volu = volu; prev_play = play; prev_prv = prv; prev_nxt = nxt;
/* 长按音量键线性渐变(同主菜单 vol_ramp 路径,每圈推进 1%)。锁屏态不刷状态栏
* (状态栏被锁屏页覆盖),仅保持真实音量/DAC 同步。 */
if (vol_ramp_dir != 0) {
int held = (vol_ramp_dir > 0)
? (kp_keys.ok && kp_is_down(&kp_keys, KEY_VOLUMEUP))
: (kp_keys.ok && kp_is_down(&kp_keys, KEY_VOLUMEDOWN));
int going = vol_ramp_tick(held);
if (!going) logmsg("lock: volume ramp end -> %d", settings_volume_get());
}
/* 空闲超时 → 自睡(锁态保持,仍在锁屏循环,唤醒回锁屏页) */
if (meow_screen_on && now_ms() - last_act >= lk_off_ms) {
logmsg("lock: idle %ds -> self screen off (stay locked)", lk_off_sec);
@@ -265,8 +487,6 @@ int run_lock_screen(void)
}
}
}
(void)kp_is_down(&kp_keys, KEY_VOLUMEDOWN);
(void)kp_is_down(&kp_keys, KEY_VOLUMEUP);
/* 触摸:只认键盘 tap(滑动手势忽略:不左缘返回、不通知中心下拉);触摸即输入活动,重置自睡计时 */
if (tfd >= 0 && !asleep) {
@@ -280,17 +500,24 @@ int run_lock_screen(void)
if (type == EV_ABS) {
if (code == ABS_X || code == ABS_MT_POSITION_X) mt_abs(&mt, val, mt.last_y);
else if (code == ABS_Y || code == ABS_MT_POSITION_Y) mt_abs(&mt, mt.last_x, val);
/* M7.1b 跟手指按压态:down 且两轴有效才定键(goodix 时序免疫);
* 键变化(滑动换键)即时重绘按压高亮。 */
if (mt.down && mt.last_x >= 0 && mt.last_y >= 0) {
if (lk_set_down_key(lk_key_value_at(mt.last_x, mt.last_y)))
redraw = 1;
}
} else if (type == EV_KEY && code == BTN_TOUCH) {
if (val && !mt.down) {
mt_down(&mt);
} else if (!val && mt.down) {
lk_set_down_key(LK_DOWN_NONE); /* M7.1b:抬手清按压态 */
redraw = 1;
int dx, dy;
if (mt_up(&mt, &dx, &dy)) {
int handled = 0;
for (int r = 0; r < LK_ROWS && !handled; r++) {
for (int c = 0; c < LK_COLS && !handled; c++) {
int x, y, w, h; lk_key_rect(r, c, &x, &y, &w, &h);
if (!mt_in(&mt, x, y, w, h)) continue;
if (!lk_key_hit(&mt, r, c)) continue; /* M7.1b SDK 控件命中 */
handled = 1;
int key = lk_keys[r][c];
if (lock_until > 0) break; /* 锁定期间键盘不响应 */
@@ -339,6 +566,17 @@ int run_lock_screen(void)
usleep(POLL_MS * 1000);
}
done:
meow_ui_activity_pop(); /* M7.1b:锁屏控件 owner 闸门关闭(与进场 push 对称) */
g_lk_down_key = LK_DOWN_NONE;
/* M7.1 解锁恢复:把锁屏首扑前快照的底层帧整帧回写 back_buf + present,强制底层 App/主界面
* 完整重绘(消除「解锁后整屏 = PIN 最后一帧,仅 App 控件自绘区刷新」的脏区残留)。 */
if (unlocked && lk_bg_snap && back_buf_ok && fb && lk_bg_len > 0) {
memcpy(fb, lk_bg_snap, lk_bg_len);
meow_dirty_full();
meow_ui_flush();
logmsg("lock: unlock -> restore underlying frame (full repaint)");
}
if (lk_bg_snap) { free(lk_bg_snap); lk_bg_snap = NULL; lk_bg_len = 0; }
if (tfd >= 0) {
int grab = 0; /* 显式释放 EVIOCGRAB(关闭 tfd 也会自动解除,双保险) */
ioctl(tfd, EVIOCGRAB, &grab);
@@ -349,6 +587,9 @@ done:
logmsg("lock: exit unlocked=%d screen_on=%d", unlocked, meow_screen_on);
return unlocked;
died:
meow_ui_activity_pop(); /* M7.1b:锁屏控件 owner 闸门关闭(与进场 push 对称) */
g_lk_down_key = LK_DOWN_NONE;
if (lk_bg_snap) { free(lk_bg_snap); lk_bg_snap = NULL; lk_bg_len = 0; }
if (tfd >= 0) {
int grab = 0;
ioctl(tfd, EVIOCGRAB, &grab);