- 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 推卡自刷验收全绿
747 lines
38 KiB
C
747 lines
38 KiB
C
/* lock_screen_ui.c — MeowOS 锁屏页(系统级全屏页面:launcher 渲染,App 不可见不可触)。
|
||
*
|
||
* 用户定案(2026-09-02):
|
||
* - 息屏唤醒后(默认开,system.pin_lock_on_wake=1 可关)+ 电源菜单「锁定」手动锁;开机不锁。
|
||
* - 数字键盘直接用 SDK 按钮画(不调 IME);输入 4 位自动校验;错 5 次锁 5 分钟(持久化)。
|
||
* - 正确 → 进主屏(return 1);锁屏页期间电源键=息屏(允许再睡,return 0),音量键不动;
|
||
* 不响应左缘返回 / 通知中心手势(本函数不实现这些手势,触摸只认键盘 tap)。
|
||
* - 输入不回显数字(● 遮罩)。兜底见 lock_screen.c(SSH root 删 system.pin 即解锁)。
|
||
*
|
||
* 渲染/输入循环与电源菜单同构(meow_touch + kd_* + kp_*),阻塞到解锁或电源息屏。
|
||
* 纯逻辑在 lock_screen.c(可宿主单测);本文件只做 UI 与输入胶水。
|
||
*/
|
||
#include "meow_launcher_internal.h"
|
||
#include "overlay_gate.h" /* OVERLAY_LOCK_COVER_MS(锁屏重铺阈值,与单测同源) */
|
||
|
||
/* §310 残影:锁屏处于 App 前台叠加会话时置位当前前台 App pid(run_app_lock_overlay 写入)。
|
||
* run_lock_screen 每圈据此探测 App 是否已死(组合 App 冻结缓冲 evdev 会在解锁瞬间回放成点击 /
|
||
* 叠加期 OOM 杀让 fb0 留残帧——探到死即中止锁屏,交回 waitpid 循环回收 + run_menu 重绘主屏)。
|
||
* 菜单直接锁屏(无 App)时保持 0 = 不探测。 */
|
||
int g_lock_overlay_pid = 0;
|
||
|
||
/* ---- 键盘几何(480×720):3 列×4 行圆角按钮 ---- */
|
||
#define LK_X0 32 /* 首列左缘 */
|
||
#define LK_COLW 130 /* 列宽 */
|
||
#define LK_COLG 8 /* 列间隙 */
|
||
#define LK_Y0 320 /* 首行顶缘 */
|
||
#define LK_ROW_H 82 /* 行高 */
|
||
#define LK_ROWG 10 /* 行间隙 */
|
||
#define LK_ROWS 4
|
||
#define LK_COLS 3
|
||
/* 12 键(行优先):digit=-2 清空 / -1 退格 / >=0 数字 */
|
||
#define LK_ACT_CLR (-2)
|
||
#define LK_ACT_BS (-1)
|
||
static const int lk_keys[LK_ROWS][LK_COLS] = {
|
||
{ 1, 2, 3 },
|
||
{ 4, 5, 6 },
|
||
{ 7, 8, 9 },
|
||
{ 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)
|
||
{
|
||
*x = LK_X0 + col * (LK_COLW + LK_COLG);
|
||
*y = LK_Y0 + row * (LK_ROW_H + LK_ROWG);
|
||
*w = LK_COLW;
|
||
*h = LK_ROW_H;
|
||
}
|
||
|
||
/* 居中文本(真实字体优先;回退 ASCII 点阵) */
|
||
static void lk_text(int cx, int y, const char *s, int size, uint32_t color)
|
||
{
|
||
if (mfont_ready) {
|
||
int w = meow_font_width_text(s, size);
|
||
meow_font_drawtext((uint16_t *)fb, cx - w / 2, y, s, rgb565(color), size);
|
||
} else {
|
||
draw_text(cx - (int)strlen(s) * 3, y + 1, s, 1, color);
|
||
}
|
||
}
|
||
|
||
/* 单个 PIN 圆点:filled=1 实心(已输入),0=空心(未输入)。 */
|
||
static void lk_pin_dot(int cx, int cy, int r, int filled, uint32_t ink, uint32_t bg)
|
||
{
|
||
if (filled) {
|
||
fill_circle(cx, cy, r, ink);
|
||
} else {
|
||
fill_circle(cx, cy, r, ink); /* 外圈(描边) */
|
||
fill_circle(cx, cy, r - 3, bg); /* 内孔(表面底色) */
|
||
}
|
||
}
|
||
|
||
static void lk_draw_pin(int entered, const meow_ui_theme_t *th)
|
||
{
|
||
int cx = fb_xres / 2;
|
||
int y = 210;
|
||
int gap = 48, r = 20;
|
||
int x0 = cx - (3 * gap) / 2; /* 首圆圆心;四圆等距 gap */
|
||
for (int i = 0; i < LCK_PIN_LEN; i++)
|
||
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();
|
||
fill_rect(0, 0, fb_xres, fb_yres, th->surface); /* 全屏表面底色 */
|
||
lk_text(fb_xres / 2, 120, locked_until > 0 ? "已锁定" : "输入 PIN 解锁", 24, th->on_surface);
|
||
lk_draw_pin(entered, th);
|
||
lk_text(fb_xres / 2, 260, status, 18, status[0] ? th->error : th->on_surface_variant);
|
||
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);
|
||
fill_round_rect(x, y, w, h, 12, th->surface_container);
|
||
int key = lk_keys[r][c];
|
||
if (key >= 0) {
|
||
char d[2] = { (char)('0' + key), 0 };
|
||
if (mfont_ready) {
|
||
int fw = meow_font_width_text(d, 30);
|
||
meow_font_drawtext((uint16_t *)fb, x + w / 2 - fw / 2,
|
||
y + LK_ROW_H / 2 - meow_font_line_height(30) / 2,
|
||
d, rgb565(th->on_surface), 30);
|
||
} else {
|
||
draw_text(x + w / 2 - 7, y + LK_ROW_H / 2 - 14, d, 2, th->on_surface);
|
||
}
|
||
} else if (key == LK_ACT_CLR) {
|
||
const char *s = "C"; const char *t = s;
|
||
if (mfont_ready) t = "清空";
|
||
lk_text(x + w / 2, y + LK_ROW_H / 2 - 9, t, 22, th->on_surface);
|
||
} else { /* LK_ACT_BS */
|
||
const char *t = "DEL";
|
||
if (mfont_ready) t = "退格";
|
||
lk_text(x + w / 2, y + LK_ROW_H / 2 - 9, t, 22, th->on_surface);
|
||
}
|
||
}
|
||
}
|
||
meow_dirty_full();
|
||
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)
|
||
{
|
||
int fbfd = open(FB_DEV, O_RDWR);
|
||
if (fbfd >= 0) { ioctl(fbfd, FBIOBLANK, 0); close(fbfd); } /* 亮起面板 */
|
||
meow_screen_on = 1;
|
||
fb_pan_buf0_force();
|
||
|
||
meow_touch mt; mt_reset(&mt);
|
||
int tfd = open(DEV_TOUCH, O_RDONLY | O_NONBLOCK);
|
||
/* §310 输入穿透(媒体保活 App 不被 SIGSTOP,实时读 evdev):锁屏页独占触控设备——
|
||
* EVIOCGRAB 使本 fd 之外的其它进程(保活 App 的 SDK 输入线程 / watchdog 手势)在该设备
|
||
* 上收不到事件(事件仅投递抓取者),且抓取期事件不会为未抓取 fd 缓冲/回放。由此无论 App
|
||
* 冻结与否,锁屏期**任何** App 都收不到触摸(真全模态)。失败(驱动不支持)则记日志降级,
|
||
* 仍靠 keep_running/SIGSTOP+标记隔离。关闭 tfd 时抓取自动解除(仍显式释放一次)。 */
|
||
if (tfd >= 0) {
|
||
int grab = 1;
|
||
if (ioctl(tfd, EVIOCGRAB, &grab) < 0)
|
||
logmsg("lock: EVIOCGRAB touch fail: %s (graceful degrade, marker+SIGSTOP isola only)",
|
||
strerror(errno));
|
||
}
|
||
struct keypoll kp_power, kp_keys;
|
||
kp_open(&kp_power, DEV_POWER);
|
||
kp_open(&kp_keys, DEV_KEYS);
|
||
|
||
int entered = 0;
|
||
char digits[LCK_PIN_LEN + 1] = {0};
|
||
char status[64] = "";
|
||
time_t lock_until = 0;
|
||
{
|
||
time_t rem = lck_lock_remaining();
|
||
if (rem > 0) { lock_until = time(NULL) + rem; snprintf(status, sizeof status, "已锁定"); }
|
||
}
|
||
time_t last_countdown = 0;
|
||
|
||
/* 面板一次性解锁恢复口:进入时消费(存在即删除+放行一次,不校验 PIN/不清 PIN/不重置失败计数) */
|
||
if (lck_unlock_once_consume()) {
|
||
lck_manual_lock_clear(); /* 进入系统即视为解除主动锁 */
|
||
logmsg("lock: unlock-once bypass consumed at entry -> release");
|
||
return 1;
|
||
}
|
||
|
||
/* 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();
|
||
int covered_once = 0; /* §310 问题2-②:标记节流窗后重铺一次全屏底,盖掉 App 残帧 */
|
||
/* §310 M6.7 锁屏页自睡(294 遗留):锁屏页不应常亮耗电。
|
||
* 阈值 = min(系统 screen_off_timeout, 30s):系统未设/设 0(常亮)/超 30 → 锁屏页封顶 30s,
|
||
* 系统设更短则尊重更短。超阈值 → meow_panel_off()(与息屏路径一致)+ asleep=1(锁态保持,
|
||
* 仍留在锁屏循环);asleep 后电源键按下边沿/触摸活动 → meow_panel_on() + 重画锁屏页回锁。 */
|
||
int lk_off_sec = overlay_lock_idle_off_sec(screen_off_timeout);
|
||
unsigned long long lk_off_ms = (unsigned long long)lk_off_sec * 1000;
|
||
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;
|
||
|
||
/* 面板一次性解锁恢复口:锁屏页停留期(每次重试前/每轮)也消费 */
|
||
if (lck_unlock_once_consume()) {
|
||
lck_manual_lock_clear();
|
||
logmsg("lock: unlock-once bypass consumed in-loop -> release");
|
||
unlocked = 1;
|
||
goto done;
|
||
}
|
||
|
||
/* §310 问题2-②:媒体保活 App 不冻结、SDK present 抑制有 ≤200ms 标记节流窗——锁屏首扑后
|
||
* App 仍可能再进 1-2 帧(部分脏区盖到锁屏上)。等标记窗过后(≥300ms)重铺一次全屏不透明底
|
||
* 盖掉 App 残帧;App 此后被抑制不再上屏,锁屏稳定遮盖(真机「App 控件盖到锁屏上」)。 */
|
||
if (!covered_once && !asleep && now_ms() - entry_ms >= OVERLAY_LOCK_COVER_MS) {
|
||
covered_once = 1;
|
||
logmsg("lock: re-cover full opaque after marker window (app stave suppressed)");
|
||
lk_draw(entered, status, lock_until);
|
||
}
|
||
|
||
/* §310 残影:锁屏叠加期 App 崩溃(僵尸)→ 中止锁屏走统一退出路径(交回 waitpid 循环
|
||
* 回收 + run_menu 重绘主屏含状态栏)。仅当 run_app_lock_overlay 置位 g_lock_overlay_pid
|
||
* 才探(菜单直接锁屏无 App 可死,恒不中止)。 */
|
||
if (g_lock_overlay_pid > 0 &&
|
||
lck_overlay_should_abort(g_lock_overlay_pid, proc_is_zombie(g_lock_overlay_pid))) {
|
||
logmsg("lock: overlay app pid %d died during lock -> abort (unified exit path)",
|
||
g_lock_overlay_pid);
|
||
g_lock_overlay_pid = 0;
|
||
goto died;
|
||
}
|
||
|
||
/* 锁定到期自动解禁(清计数+戳),仅重画状态行 */
|
||
if (lock_until > 0 && now >= lock_until) {
|
||
lck_lock_remaining();
|
||
lock_until = 0;
|
||
status[0] = 0;
|
||
redraw = 1;
|
||
}
|
||
if (lock_until > 0 && now != last_countdown) {
|
||
last_countdown = now;
|
||
time_t rem = lock_until - now;
|
||
if (mfont_ready) snprintf(status, sizeof status, "已锁定,请 %d:%02d 后重试",
|
||
(int)(rem / 60), (int)(rem % 60));
|
||
else snprintf(status, sizeof status, "LOCKED %d:%02d", (int)(rem / 60), (int)(rem % 60));
|
||
redraw = 1;
|
||
}
|
||
|
||
/* 电源键/音量键轮询 + 输入活动检测(重置自睡计时) + 自睡/唤醒切换。
|
||
* 活跃态:电源键按下 → 息屏并退出锁屏(回 App/主屏,下次唤醒再锁)——沿用原语义;
|
||
* 空闲超 lk_off_ms → 自睡(asleep=1,锁态保持,仍在本循环)。
|
||
* asleep:电源键按下边沿 / 触摸活动 → meow_panel_on + 重画锁屏页(本循环唤醒链回锁)。
|
||
* 音量键本页不动(仅视为输入活动,重置计时)。 */
|
||
{
|
||
int chg = 0;
|
||
if (kp_power.ok) chg |= kp_poll(&kp_power, NULL, NULL);
|
||
chg |= kp_poll(&kp_keys, NULL, NULL);
|
||
if (chg) last_act = now_ms(); /* 任意按键活动重置自睡计时 */
|
||
int pwr_down = kp_is_down(&kp_power, KEY_POWER);
|
||
int pwr_edge = pwr_down && !prev_pwr_down; /* 按下边沿 */
|
||
prev_pwr_down = pwr_down;
|
||
|
||
if (asleep) {
|
||
int touch_ready = (tfd >= 0);
|
||
if (touch_ready) {
|
||
struct pollfd tp = { tfd, POLLIN, 0 };
|
||
if (poll(&tp, 1, 0) <= 0) touch_ready = 0;
|
||
}
|
||
if (pwr_edge || touch_ready) {
|
||
logmsg("lock: wake from self-sleep (power=%d touch=%d)", pwr_edge, touch_ready);
|
||
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;
|
||
}
|
||
} else {
|
||
/* 电源键=息屏(允许再睡;下次唤醒按 lck_wake_should_lock 再锁) */
|
||
if (pwr_down) {
|
||
logmsg("lock: power key -> screen off (stay locked)");
|
||
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);
|
||
meow_panel_off();
|
||
asleep = 1;
|
||
continue;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 触摸:只认键盘 tap(滑动手势忽略:不左缘返回、不通知中心下拉);触摸即输入活动,重置自睡计时 */
|
||
if (tfd >= 0 && !asleep) {
|
||
struct pollfd pfd = { tfd, POLLIN, 0 };
|
||
if (poll(&pfd, 1, 0) > 0) {
|
||
last_act = now_ms(); /* §310 M6.7:任意触摸重置自睡计时 */
|
||
unsigned char ev[16];
|
||
while (read(tfd, ev, sizeof ev) == (ssize_t)sizeof ev) {
|
||
uint16_t type = *(uint16_t *)(ev + 8), code = *(uint16_t *)(ev + 10);
|
||
int32_t val = *(int32_t *)(ev + 12);
|
||
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++) {
|
||
if (!lk_key_hit(&mt, r, c)) continue; /* M7.1b SDK 控件命中 */
|
||
handled = 1;
|
||
int key = lk_keys[r][c];
|
||
if (lock_until > 0) break; /* 锁定期间键盘不响应 */
|
||
if (key >= 0) {
|
||
if (entered < LCK_PIN_LEN) {
|
||
digits[entered++] = (char)('0' + key);
|
||
digits[entered] = 0;
|
||
}
|
||
} else if (key == LK_ACT_CLR) {
|
||
entered = 0; digits[0] = 0;
|
||
} else if (key == LK_ACT_BS) {
|
||
if (entered > 0) { entered--; digits[entered] = 0; }
|
||
}
|
||
redraw = 1;
|
||
if (entered == LCK_PIN_LEN) {
|
||
if (lck_pin_check(digits)) {
|
||
lck_pin_success();
|
||
lck_manual_lock_clear(); /* 主动锁标志消解 */
|
||
logmsg("lock: PIN OK -> unlock");
|
||
unlocked = 1;
|
||
goto done;
|
||
} else {
|
||
int rem = lck_pin_fail();
|
||
entered = 0; digits[0] = 0;
|
||
if (rem > 0) {
|
||
lock_until = time(NULL) + rem;
|
||
last_countdown = 0;
|
||
snprintf(status, sizeof status, "已锁定");
|
||
} else {
|
||
if (mfont_ready) snprintf(status, sizeof status, "PIN 错误");
|
||
else snprintf(status, sizeof status, "WRONG PIN");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 整页重绘(锁屏低频交互,简单整页即可);自睡(面板已关)期间不重绘(唤醒时统一重画) */
|
||
if (redraw && !asleep) lk_draw(entered, status, lock_until);
|
||
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);
|
||
close(tfd);
|
||
}
|
||
kp_close(&kp_power);
|
||
kp_close(&kp_keys);
|
||
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);
|
||
close(tfd);
|
||
}
|
||
kp_close(&kp_power);
|
||
kp_close(&kp_keys);
|
||
logmsg("lock: exit overlay-app-died -> LCK_OVERLAY_RET_DIED");
|
||
return LCK_OVERLAY_RET_DIED;
|
||
}
|
||
|
||
/* 电源菜单「锁定」:**主动锁屏**(用户定案 2026-09-02 强化)。
|
||
* 语义 = 立即熄屏 + 进入锁定 + 作废挂起的面板绕过标志:
|
||
* ① rm /tmp/meow_pin_unlock_once(主动锁屏作废一次性绕过);
|
||
* ② 写 /tmp/meow_pin_locked(主动锁标志 → 唤醒时无论 system.pin_lock_on_wake 是否开都必锁屏);
|
||
* ③ meow_panel_off() 立即熄屏(唤醒时经主屏唤醒路径弹锁屏页)。
|
||
* 不立即弹锁屏页(已熄屏);未配凭据 → no-op。返回 0(关菜单,run_menu 继续)。 */
|
||
int meow_lock_screen_engage(void)
|
||
{
|
||
if (!lck_pin_configured()) {
|
||
logmsg("lock: no system.pin/panel.pin configured -> skip manual lock (no-op)");
|
||
return 0;
|
||
}
|
||
if (access(LCK_UNLOCK_ONCE_FILE, F_OK) == 0) {
|
||
unlink(LCK_UNLOCK_ONCE_FILE);
|
||
logmsg("lock: manual active-lock revoked pending unlock-once bypass");
|
||
}
|
||
lck_manual_lock_set();
|
||
meow_panel_off(); /* 立即熄屏;唤醒时必走锁屏 */
|
||
logmsg("lock: manual active-lock -> screen off + lock on wake");
|
||
return 0;
|
||
}
|
||
|
||
/* ---- 设置页「锁屏 PIN」数字输入模态(system 级 PIN 设置,用 SDK 按钮画数字键盘)----
|
||
* 全屏遮罩:title + 4 圆点(●)+ 数字键盘(1-9 / C / 0 / 退格)。输入满 4 位即返回 1 并拷贝到 out
|
||
* (不校验——调用方做双输确认/旧 PIN 校验);电源键=取消返回 0;音量键不动。 */
|
||
int lck_input_pin_modal(const char *title, char *out, int outcap)
|
||
{
|
||
meow_screen_on = 1;
|
||
fb_pan_buf0_force();
|
||
meow_touch mt; mt_reset(&mt);
|
||
int tfd = open(DEV_TOUCH, O_RDONLY | O_NONBLOCK);
|
||
/* §310 输入穿透:设置页 PIN 模态同样独占触控设备(后台保活 App 不冻结实时读 evdev,防其
|
||
* 同坐标控件被 PIN 键盘点击双重触发)。失败降级(仍靠标记+SIGSTOP 隔离)。 */
|
||
if (tfd >= 0) {
|
||
int grab = 1;
|
||
if (ioctl(tfd, EVIOCGRAB, &grab) < 0)
|
||
logmsg("pin-modal: EVIOCGRAB touch fail: %s (graceful degrade)", strerror(errno));
|
||
}
|
||
struct keypoll kp_power, kp_keys;
|
||
kp_open(&kp_power, DEV_POWER);
|
||
kp_open(&kp_keys, DEV_KEYS);
|
||
|
||
int entered = 0;
|
||
char digits[LCK_PIN_LEN + 1] = {0};
|
||
int result = 0;
|
||
int dirty = 1; /* 首帧必画 */
|
||
|
||
for (;;) {
|
||
/* 只在首帧/输入变化时整页重绘(消频闪:锁屏低频交互),后续轮次不动 LCDC */
|
||
if (dirty) {
|
||
const meow_ui_theme_t *th = s_ui_theme();
|
||
fill_rect(0, 0, fb_xres, fb_yres, th->surface);
|
||
lk_text(fb_xres / 2, 120, title, 24, th->on_surface);
|
||
lk_draw_pin(entered, th);
|
||
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);
|
||
fill_round_rect(x, y, w, h, 12, th->surface_container);
|
||
int key = lk_keys[r][c];
|
||
if (key >= 0) {
|
||
char d[2] = { (char)('0' + key), 0 };
|
||
if (mfont_ready) {
|
||
int fw = meow_font_width_text(d, 30);
|
||
meow_font_drawtext((uint16_t *)fb, x + w / 2 - fw / 2,
|
||
y + LK_ROW_H / 2 - meow_font_line_height(30) / 2,
|
||
d, rgb565(th->on_surface), 30);
|
||
} else {
|
||
draw_text(x + w / 2 - 7, y + LK_ROW_H / 2 - 14, d, 2, th->on_surface);
|
||
}
|
||
} else if (key == LK_ACT_CLR) {
|
||
const char *t2 = "C"; if (mfont_ready) t2 = "清空";
|
||
lk_text(x + w / 2, y + LK_ROW_H / 2 - 9, t2, 22, th->on_surface);
|
||
} else {
|
||
const char *t2 = "DEL"; if (mfont_ready) t2 = "退格";
|
||
lk_text(x + w / 2, y + LK_ROW_H / 2 - 9, t2, 22, th->on_surface);
|
||
}
|
||
}
|
||
}
|
||
meow_dirty_full();
|
||
fb_pan_buf0_force();
|
||
meow_ui_flush();
|
||
dirty = 0;
|
||
}
|
||
|
||
if (kp_power.ok) {
|
||
kp_poll(&kp_power, NULL, NULL);
|
||
if (kp_is_down(&kp_power, KEY_POWER)) { result = 0; break; } /* 取消 */
|
||
}
|
||
kp_poll(&kp_keys, NULL, NULL);
|
||
(void)kp_is_down(&kp_keys, KEY_VOLUMEDOWN);
|
||
(void)kp_is_down(&kp_keys, KEY_VOLUMEUP);
|
||
|
||
if (tfd >= 0) {
|
||
struct pollfd pfd = { tfd, POLLIN, 0 };
|
||
if (poll(&pfd, 1, 0) > 0) {
|
||
unsigned char ev[16];
|
||
while (read(tfd, ev, sizeof ev) == (ssize_t)sizeof ev) {
|
||
uint16_t type = *(uint16_t *)(ev + 8), code = *(uint16_t *)(ev + 10);
|
||
int32_t val = *(int32_t *)(ev + 12);
|
||
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);
|
||
} else if (type == EV_KEY && code == BTN_TOUCH) {
|
||
if (val && !mt.down) { mt_down(&mt); }
|
||
else if (!val && mt.down) {
|
||
int dx, dy;
|
||
if (mt_up(&mt, &dx, &dy)) {
|
||
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);
|
||
if (!mt_in(&mt, x, y, w, h)) continue;
|
||
int key = lk_keys[r][c];
|
||
if (key >= 0) {
|
||
if (entered < LCK_PIN_LEN) { digits[entered++] = (char)('0' + key); digits[entered] = 0; }
|
||
} else if (key == LK_ACT_CLR) { entered = 0; digits[0] = 0; }
|
||
else if (key == LK_ACT_BS) { if (entered > 0) { entered--; digits[entered] = 0; } }
|
||
dirty = 1;
|
||
if (entered == LCK_PIN_LEN) {
|
||
if (out && outcap > LCK_PIN_LEN) { snprintf(out, (size_t)outcap, "%s", digits); result = 1; }
|
||
else result = 1;
|
||
goto done2;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
usleep(POLL_MS * 1000);
|
||
}
|
||
done2:
|
||
if (tfd >= 0) {
|
||
int grab = 0; /* 显式释放 EVIOCGRAB(关闭 tfd 亦自动解除,双保险) */
|
||
ioctl(tfd, EVIOCGRAB, &grab);
|
||
close(tfd);
|
||
}
|
||
kp_close(&kp_power);
|
||
kp_close(&kp_keys);
|
||
return result;
|
||
}
|